public synchronized Object get(String name) {
   Object o = map.get(name);
   if (o == AvaticaParameter.DUMMY_VALUE) {
     return null;
   }
   return o;
 }
    DataContextImpl(OptiqConnectionImpl connection, List<Object> parameterValues) {
      this.queryProvider = connection;
      this.typeFactory = connection.getTypeFactory();
      this.rootSchema = connection.rootSchema;

      // Store the time at which the query started executing. The SQL
      // standard says that functions such as CURRENT_TIMESTAMP return the
      // same value throughout the query.
      final long time = System.currentTimeMillis();
      final TimeZone timeZone = connection.getTimeZone();
      final long localOffset = timeZone.getOffset(time);
      final long currentOffset = localOffset;

      ImmutableMap.Builder<Object, Object> builder = ImmutableMap.builder();
      builder
          .put("utcTimestamp", time)
          .put("currentTimestamp", time + currentOffset)
          .put("localTimestamp", time + localOffset)
          .put("timeZone", timeZone);
      for (Ord<Object> value : Ord.zip(parameterValues)) {
        Object e = value.e;
        if (e == null) {
          e = AvaticaParameter.DUMMY_VALUE;
        }
        builder.put("?" + value.i, e);
      }
      map = builder.build();
    }
Example #3
0
 private ImmutableMap<String, JdbcTable> computeTables() {
   Connection connection = null;
   ResultSet resultSet = null;
   try {
     connection = dataSource.getConnection();
     DatabaseMetaData metaData = connection.getMetaData();
     resultSet = metaData.getTables(catalog, schema, null, null);
     final ImmutableMap.Builder<String, JdbcTable> builder = ImmutableMap.builder();
     while (resultSet.next()) {
       final String tableName = resultSet.getString(3);
       final String catalogName = resultSet.getString(1);
       final String schemaName = resultSet.getString(2);
       final String tableTypeName = resultSet.getString(4);
       // Clean up table type. In particular, this ensures that 'SYSTEM TABLE',
       // returned by Phoenix among others, maps to TableType.SYSTEM_TABLE.
       // We know enum constants are upper-case without spaces, so we can't
       // make things worse.
       final String tableTypeName2 = tableTypeName.toUpperCase().replace(' ', '_');
       final TableType tableType = Util.enumVal(TableType.class, tableTypeName2);
       final JdbcTable table = new JdbcTable(this, catalogName, schemaName, tableName, tableType);
       builder.put(tableName, table);
     }
     return builder.build();
   } catch (SQLException e) {
     throw new RuntimeException("Exception while reading tables", e);
   } finally {
     close(connection, null, resultSet);
   }
 }
Example #4
0
 public ImmutableMap<ColumnIdent, String> analyzers() {
   Map<String, Object> propertiesMap = getNested(defaultMappingMap, "properties");
   if (propertiesMap == null) {
     return ImmutableMap.of();
   } else {
     return getAnalyzers(null, propertiesMap);
   }
 }
Example #5
0
  /**
   * Returns true if the schema of this and <code>other</code> is the same, this includes the table
   * name, as this is reflected in the ReferenceIdents of the columns.
   */
  public boolean schemaEquals(DocIndexMetaData other) {
    if (this == other) return true;
    if (other == null) return false;

    // TODO: when analyzers are exposed in the info, equality has to be checked on them
    // see: TransportSQLActionTest.testSelectTableAliasSchemaExceptionColumnDefinition
    if (columns != null ? !columns.equals(other.columns) : other.columns != null) return false;
    if (primaryKey != null ? !primaryKey.equals(other.primaryKey) : other.primaryKey != null)
      return false;
    if (indices != null ? !indices.equals(other.indices) : other.indices != null) return false;
    if (references != null ? !references.equals(other.references) : other.references != null)
      return false;
    if (routingCol != null ? !routingCol.equals(other.routingCol) : other.routingCol != null)
      return false;

    return true;
  }
Example #6
0
 private ImmutableMap<ColumnIdent, IndexReferenceInfo> createIndexDefinitions() {
   ImmutableMap.Builder<ColumnIdent, IndexReferenceInfo> builder = ImmutableMap.builder();
   for (Map.Entry<ColumnIdent, IndexReferenceInfo.Builder> entry : indicesBuilder.entrySet()) {
     builder.put(entry.getKey(), entry.getValue().build());
   }
   indices = builder.build();
   return indices;
 }
 /** Returns document counts for each partition. */
 Map<Object, Integer> getDocumentCountByPartition(List<Document> documents) {
   return ImmutableMap.copyOf(
       Maps.transformValues(
           getDocumentsByPartition(documents).asMap(),
           new Function<Collection<Document>, Integer>() {
             public Integer apply(Collection<Document> documents) {
               return documents.size();
             }
           }));
 }
Example #8
0
 private void prepareCrateMeta() {
   metaMap = getNested(defaultMappingMap, "_meta");
   if (metaMap != null) {
     indicesMap = getNested(metaMap, "indices");
     if (indicesMap == null) {
       indicesMap = ImmutableMap.of();
     }
     metaColumnsMap = getNested(metaMap, "columns");
     if (metaColumnsMap == null) {
       metaColumnsMap = ImmutableMap.of();
     }
     partitionedByList = getNested(metaMap, "partitioned_by");
     if (partitionedByList == null) {
       partitionedByList = ImmutableList.of();
     }
   } else {
     metaMap = new HashMap<>();
     indicesMap = new HashMap<>();
     metaColumnsMap = new HashMap<>();
     partitionedByList = ImmutableList.of();
   }
 }
Example #9
0
  /**
   * extract dataType from given columnProperties
   *
   * @param columnProperties map of String to Object containing column properties
   * @return dataType of the column with columnProperties
   */
  public static DataType getColumnDataType(Map<String, Object> columnProperties) {
    DataType type;
    String typeName = (String) columnProperties.get("type");

    if (typeName == null) {
      if (columnProperties.containsKey("properties")) {
        type = DataTypes.OBJECT;
      } else {
        return DataTypes.NOT_SUPPORTED;
      }
    } else if (typeName.equalsIgnoreCase("array")) {

      Map<String, Object> innerProperties = getNested(columnProperties, "inner");
      DataType innerType = getColumnDataType(innerProperties);
      type = new ArrayType(innerType);
    } else {
      typeName = typeName.toLowerCase(Locale.ENGLISH);
      type = MoreObjects.firstNonNull(dataTypeMap.get(typeName), DataTypes.NOT_SUPPORTED);
    }
    return type;
  }
Example #10
0
 private ImmutableMap<ColumnIdent, String> getAnalyzers(
     ColumnIdent columnIdent, Map<String, Object> propertiesMap) {
   ImmutableMap.Builder<ColumnIdent, String> builder = ImmutableMap.builder();
   for (Map.Entry<String, Object> columnEntry : propertiesMap.entrySet()) {
     Map<String, Object> columnProperties = (Map) columnEntry.getValue();
     DataType columnDataType = getColumnDataType(columnProperties);
     ColumnIdent newIdent = childIdent(columnIdent, columnEntry.getKey());
     columnProperties = furtherColumnProperties(columnProperties);
     if (columnDataType == DataTypes.OBJECT
         || (columnDataType.id() == ArrayType.ID
             && ((ArrayType) columnDataType).innerType() == DataTypes.OBJECT)) {
       if (columnProperties.get("properties") != null) {
         builder.putAll(
             getAnalyzers(newIdent, (Map<String, Object>) columnProperties.get("properties")));
       }
     }
     String analyzer = (String) columnProperties.get("analyzer");
     if (analyzer != null) {
       builder.put(newIdent, analyzer);
     }
   }
   return builder.build();
 }
Example #11
0
public class MapperService extends AbstractIndexComponent implements Iterable<DocumentMapper> {

  public static final String DEFAULT_MAPPING = "_default_";

  private final AnalysisService analysisService;

  /** Will create types automatically if they do not exists in the mapping definition yet */
  private final boolean dynamic;

  private volatile String defaultMappingSource;
  private volatile String defaultPercolatorMappingSource;

  private volatile Map<String, DocumentMapper> mappers = ImmutableMap.of();

  private final Object typeMutex = new Object();
  private final Object mappersMutex = new Object();

  private final FieldMappersLookup fieldMappers = new FieldMappersLookup();
  private volatile ImmutableOpenMap<String, ObjectMappers> fullPathObjectMappers =
      ImmutableOpenMap.of();
  private boolean hasNested = false; // updated dynamically to true when a nested object is added

  private final DocumentMapperParser documentParser;

  private final InternalFieldMapperListener fieldMapperListener = new InternalFieldMapperListener();
  private final InternalObjectMapperListener objectMapperListener =
      new InternalObjectMapperListener();

  private final SmartIndexNameSearchAnalyzer searchAnalyzer;
  private final SmartIndexNameSearchQuoteAnalyzer searchQuoteAnalyzer;

  private final List<DocumentTypeListener> typeListeners =
      new CopyOnWriteArrayList<DocumentTypeListener>();

  @Inject
  public MapperService(
      Index index,
      @IndexSettings Settings indexSettings,
      Environment environment,
      AnalysisService analysisService,
      PostingsFormatService postingsFormatService,
      DocValuesFormatService docValuesFormatService,
      SimilarityLookupService similarityLookupService) {
    super(index, indexSettings);
    this.analysisService = analysisService;
    this.documentParser =
        new DocumentMapperParser(
            index,
            indexSettings,
            analysisService,
            postingsFormatService,
            docValuesFormatService,
            similarityLookupService);
    this.searchAnalyzer = new SmartIndexNameSearchAnalyzer(analysisService.defaultSearchAnalyzer());
    this.searchQuoteAnalyzer =
        new SmartIndexNameSearchQuoteAnalyzer(analysisService.defaultSearchQuoteAnalyzer());

    this.dynamic = componentSettings.getAsBoolean("dynamic", true);
    String defaultMappingLocation = componentSettings.get("default_mapping_location");
    URL defaultMappingUrl;
    if (defaultMappingLocation == null) {
      try {
        defaultMappingUrl = environment.resolveConfig("default-mapping.json");
      } catch (FailedToResolveConfigException e) {
        // not there, default to the built in one
        defaultMappingUrl =
            indexSettings
                .getClassLoader()
                .getResource("org/elasticsearch/index/mapper/default-mapping.json");
        if (defaultMappingUrl == null) {
          defaultMappingUrl =
              MapperService.class
                  .getClassLoader()
                  .getResource("org/elasticsearch/index/mapper/default-mapping.json");
        }
      }
    } else {
      try {
        defaultMappingUrl = environment.resolveConfig(defaultMappingLocation);
      } catch (FailedToResolveConfigException e) {
        // not there, default to the built in one
        try {
          defaultMappingUrl = new File(defaultMappingLocation).toURI().toURL();
        } catch (MalformedURLException e1) {
          throw new FailedToResolveConfigException(
              "Failed to resolve dynamic mapping location [" + defaultMappingLocation + "]");
        }
      }
    }

    if (defaultMappingUrl == null) {
      logger.info(
          "failed to find default-mapping.json in the classpath, using the default template");
      defaultMappingSource = "{\n" + "    \"_default_\":{\n" + "    }\n" + "}";
    } else {
      try {
        defaultMappingSource =
            Streams.copyToString(
                new InputStreamReader(defaultMappingUrl.openStream(), Charsets.UTF_8));
      } catch (IOException e) {
        throw new MapperException(
            "Failed to load default mapping source from [" + defaultMappingLocation + "]", e);
      }
    }

    String percolatorMappingLocation = componentSettings.get("default_percolator_mapping_location");
    URL percolatorMappingUrl = null;
    if (percolatorMappingLocation != null) {
      try {
        percolatorMappingUrl = environment.resolveConfig(percolatorMappingLocation);
      } catch (FailedToResolveConfigException e) {
        // not there, default to the built in one
        try {
          percolatorMappingUrl = new File(percolatorMappingLocation).toURI().toURL();
        } catch (MalformedURLException e1) {
          throw new FailedToResolveConfigException(
              "Failed to resolve default percolator mapping location ["
                  + percolatorMappingLocation
                  + "]");
        }
      }
    }
    if (percolatorMappingUrl != null) {
      try {
        defaultPercolatorMappingSource =
            Streams.copyToString(
                new InputStreamReader(percolatorMappingUrl.openStream(), Charsets.UTF_8));
      } catch (IOException e) {
        throw new MapperException(
            "Failed to load default percolator mapping source from [" + percolatorMappingUrl + "]",
            e);
      }
    } else {
      defaultPercolatorMappingSource =
          "{\n"
              +
              // "    \"" + PercolatorService.TYPE_NAME + "\":{\n" +
              "    \""
              + "_default_"
              + "\":{\n"
              + "        \"_id\" : {\"index\": \"not_analyzed\"},"
              + "        \"properties\" : {\n"
              + "            \"query\" : {\n"
              + "                \"type\" : \"object\",\n"
              + "                \"enabled\" : false\n"
              + "            }\n"
              + "        }\n"
              + "    }\n"
              + "}";
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          "using dynamic[{}], default mapping: default_mapping_location[{}], loaded_from[{}], default percolator mapping: location[{}], loaded_from[{}]",
          dynamic,
          defaultMappingLocation,
          defaultMappingUrl,
          percolatorMappingLocation,
          percolatorMappingUrl);
    } else if (logger.isTraceEnabled()) {
      logger.trace(
          "using dynamic[{}], default mapping: default_mapping_location[{}], loaded_from[{}] and source[{}], default percolator mapping: location[{}], loaded_from[{}] and source[{}]",
          dynamic,
          defaultMappingLocation,
          defaultMappingUrl,
          defaultMappingSource,
          percolatorMappingLocation,
          percolatorMappingUrl,
          defaultPercolatorMappingSource);
    }
  }

  public void close() {
    for (DocumentMapper documentMapper : mappers.values()) {
      documentMapper.close();
    }
  }

  public boolean hasNested() {
    return this.hasNested;
  }

  @Override
  public UnmodifiableIterator<DocumentMapper> iterator() {
    return Iterators.unmodifiableIterator(mappers.values().iterator());
  }

  public AnalysisService analysisService() {
    return this.analysisService;
  }

  public DocumentMapperParser documentMapperParser() {
    return this.documentParser;
  }

  public void addTypeListener(DocumentTypeListener listener) {
    typeListeners.add(listener);
  }

  public void removeTypeListener(DocumentTypeListener listener) {
    typeListeners.remove(listener);
  }

  public DocumentMapper merge(String type, String mappingSource, boolean applyDefault) {
    if (DEFAULT_MAPPING.equals(type)) {
      // verify we can parse it
      DocumentMapper mapper = documentParser.parse(type, mappingSource);
      // still add it as a document mapper so we have it registered and, for example, persisted back
      // into
      // the cluster meta data if needed, or checked for existence
      synchronized (typeMutex) {
        mappers = newMapBuilder(mappers).put(type, mapper).map();
      }
      defaultMappingSource = mappingSource;
      return mapper;
    } else {
      return merge(parse(type, mappingSource, applyDefault));
    }
  }

  // never expose this to the outside world, we need to reparse the doc mapper so we get fresh
  // instances of field mappers to properly remove existing doc mapper
  private DocumentMapper merge(DocumentMapper mapper) {
    synchronized (typeMutex) {
      if (mapper.type().length() == 0) {
        throw new InvalidTypeNameException("mapping type name is empty");
      }
      if (mapper.type().charAt(0) == '_' && !PercolatorService.TYPE_NAME.equals(mapper.type())) {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] can't start with '_'");
      }
      if (mapper.type().contains("#")) {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] should not include '#' in it");
      }
      if (mapper.type().contains(",")) {
        throw new InvalidTypeNameException(
            "mapping type name [" + mapper.type() + "] should not include ',' in it");
      }
      if (mapper.type().contains(".")) {
        logger.warn(
            "Type [{}] contains a '.', it is recommended not to include it within a type name",
            mapper.type());
      }
      // we can add new field/object mappers while the old ones are there
      // since we get new instances of those, and when we remove, we remove
      // by instance equality
      DocumentMapper oldMapper = mappers.get(mapper.type());

      if (oldMapper != null) {
        DocumentMapper.MergeResult result = oldMapper.merge(mapper, mergeFlags().simulate(false));
        if (result.hasConflicts()) {
          // TODO: What should we do???
          if (logger.isDebugEnabled()) {
            logger.debug(
                "merging mapping for type [{}] resulted in conflicts: [{}]",
                mapper.type(),
                Arrays.toString(result.conflicts()));
          }
        }
        return oldMapper;
      } else {
        FieldMapperListener.Aggregator fieldMappersAgg = new FieldMapperListener.Aggregator();
        mapper.traverse(fieldMappersAgg);
        addFieldMappers(
            fieldMappersAgg.mappers.toArray(new FieldMapper[fieldMappersAgg.mappers.size()]));
        mapper.addFieldMapperListener(fieldMapperListener, false);

        ObjectMapperListener.Aggregator objectMappersAgg = new ObjectMapperListener.Aggregator();
        mapper.traverse(objectMappersAgg);
        addObjectMappers(
            objectMappersAgg.mappers.toArray(new ObjectMapper[objectMappersAgg.mappers.size()]));
        mapper.addObjectMapperListener(objectMapperListener, false);

        mappers = newMapBuilder(mappers).put(mapper.type(), mapper).map();
        for (DocumentTypeListener typeListener : typeListeners) {
          typeListener.created(mapper.type());
        }
        return mapper;
      }
    }
  }

  private void addObjectMappers(ObjectMapper[] objectMappers) {
    synchronized (mappersMutex) {
      ImmutableOpenMap.Builder<String, ObjectMappers> fullPathObjectMappers =
          ImmutableOpenMap.builder(this.fullPathObjectMappers);
      for (ObjectMapper objectMapper : objectMappers) {
        ObjectMappers mappers = fullPathObjectMappers.get(objectMapper.fullPath());
        if (mappers == null) {
          mappers = new ObjectMappers(objectMapper);
        } else {
          mappers = mappers.concat(objectMapper);
        }
        fullPathObjectMappers.put(objectMapper.fullPath(), mappers);
        // update the hasNested flag
        if (objectMapper.nested().isNested()) {
          hasNested = true;
        }
      }
      this.fullPathObjectMappers = fullPathObjectMappers.build();
    }
  }

  private void addFieldMappers(FieldMapper[] fieldMappers) {
    synchronized (mappersMutex) {
      this.fieldMappers.addNewMappers(Arrays.asList(fieldMappers));
    }
  }

  public void remove(String type) {
    synchronized (typeMutex) {
      DocumentMapper docMapper = mappers.get(type);
      if (docMapper == null) {
        return;
      }
      docMapper.close();
      mappers = newMapBuilder(mappers).remove(type).map();
      removeObjectAndFieldMappers(docMapper);
      for (DocumentTypeListener typeListener : typeListeners) {
        typeListener.removed(type);
      }
    }
  }

  private void removeObjectAndFieldMappers(DocumentMapper docMapper) {
    synchronized (mappersMutex) {
      fieldMappers.removeMappers(docMapper.mappers());

      ImmutableOpenMap.Builder<String, ObjectMappers> fullPathObjectMappers =
          ImmutableOpenMap.builder(this.fullPathObjectMappers);
      for (ObjectMapper mapper : docMapper.objectMappers().values()) {
        ObjectMappers mappers = fullPathObjectMappers.get(mapper.fullPath());
        if (mappers != null) {
          mappers = mappers.remove(mapper);
          if (mappers.isEmpty()) {
            fullPathObjectMappers.remove(mapper.fullPath());
          } else {
            fullPathObjectMappers.put(mapper.fullPath(), mappers);
          }
        }
      }

      this.fullPathObjectMappers = fullPathObjectMappers.build();
    }
  }

  /** Just parses and returns the mapper without adding it, while still applying default mapping. */
  public DocumentMapper parse(String mappingType, String mappingSource)
      throws MapperParsingException {
    return parse(mappingType, mappingSource, true);
  }

  public DocumentMapper parse(String mappingType, String mappingSource, boolean applyDefault)
      throws MapperParsingException {
    String defaultMappingSource;
    if (PercolatorService.TYPE_NAME.equals(mappingType)) {
      defaultMappingSource = this.defaultPercolatorMappingSource;
    } else {
      defaultMappingSource = this.defaultMappingSource;
    }
    return documentParser.parse(
        mappingType, mappingSource, applyDefault ? defaultMappingSource : null);
  }

  public boolean hasMapping(String mappingType) {
    return mappers.containsKey(mappingType);
  }

  public Collection<String> types() {
    return mappers.keySet();
  }

  public DocumentMapper documentMapper(String type) {
    return mappers.get(type);
  }

  public DocumentMapper documentMapperWithAutoCreate(String type) {
    DocumentMapper mapper = mappers.get(type);
    if (mapper != null) {
      return mapper;
    }
    if (!dynamic) {
      throw new TypeMissingException(
          index, type, "trying to auto create mapping, but dynamic mapping is disabled");
    }
    // go ahead and dynamically create it
    synchronized (typeMutex) {
      mapper = mappers.get(type);
      if (mapper != null) {
        return mapper;
      }
      merge(type, null, true);
      return mappers.get(type);
    }
  }

  /**
   * A filter for search. If a filter is required, will return it, otherwise, will return
   * <tt>null</tt>.
   */
  @Nullable
  public Filter searchFilter(String... types) {
    boolean filterPercolateType = hasMapping(PercolatorService.TYPE_NAME);
    if (types != null && filterPercolateType) {
      for (String type : types) {
        if (PercolatorService.TYPE_NAME.equals(type)) {
          filterPercolateType = false;
          break;
        }
      }
    }
    Filter excludePercolatorType = null;
    if (filterPercolateType) {
      excludePercolatorType =
          new NotFilter(documentMapper(PercolatorService.TYPE_NAME).typeFilter());
    }

    if (types == null || types.length == 0) {
      if (hasNested && filterPercolateType) {
        return new AndFilter(ImmutableList.of(excludePercolatorType, NonNestedDocsFilter.INSTANCE));
      } else if (hasNested) {
        return NonNestedDocsFilter.INSTANCE;
      } else if (filterPercolateType) {
        return excludePercolatorType;
      } else {
        return null;
      }
    }
    // if we filter by types, we don't need to filter by non nested docs
    // since they have different types (starting with __)
    if (types.length == 1) {
      DocumentMapper docMapper = documentMapper(types[0]);
      if (docMapper == null) {
        return new TermFilter(new Term(types[0]));
      }
      return docMapper.typeFilter();
    }
    // see if we can use terms filter
    boolean useTermsFilter = true;
    for (String type : types) {
      DocumentMapper docMapper = documentMapper(type);
      if (docMapper == null) {
        useTermsFilter = false;
        break;
      }
      if (!docMapper.typeMapper().fieldType().indexed()) {
        useTermsFilter = false;
        break;
      }
    }

    if (useTermsFilter) {
      BytesRef[] typesBytes = new BytesRef[types.length];
      for (int i = 0; i < typesBytes.length; i++) {
        typesBytes[i] = new BytesRef(types[i]);
      }
      TermsFilter termsFilter = new TermsFilter(TypeFieldMapper.NAME, typesBytes);
      if (filterPercolateType) {
        return new AndFilter(ImmutableList.of(excludePercolatorType, termsFilter));
      } else {
        return termsFilter;
      }
    } else {
      // Current bool filter requires that at least one should clause matches, even with a must
      // clause.
      XBooleanFilter bool = new XBooleanFilter();
      for (String type : types) {
        DocumentMapper docMapper = documentMapper(type);
        if (docMapper == null) {
          bool.add(
              new FilterClause(
                  new TermFilter(new Term(TypeFieldMapper.NAME, type)),
                  BooleanClause.Occur.SHOULD));
        } else {
          bool.add(new FilterClause(docMapper.typeFilter(), BooleanClause.Occur.SHOULD));
        }
      }
      if (filterPercolateType) {
        bool.add(excludePercolatorType, BooleanClause.Occur.MUST);
      }

      return bool;
    }
  }

  /**
   * Returns {@link FieldMappers} for all the {@link FieldMapper}s that are registered under the
   * given name across all the different {@link DocumentMapper} types.
   *
   * @param name The name to return all the {@link FieldMappers} for across all {@link
   *     DocumentMapper}s.
   * @return All the {@link FieldMappers} for across all {@link DocumentMapper}s
   */
  public FieldMappers name(String name) {
    return fieldMappers.name(name);
  }

  /**
   * Returns {@link FieldMappers} for all the {@link FieldMapper}s that are registered under the
   * given indexName across all the different {@link DocumentMapper} types.
   *
   * @param indexName The indexName to return all the {@link FieldMappers} for across all {@link
   *     DocumentMapper}s.
   * @return All the {@link FieldMappers} across all {@link DocumentMapper}s for the given
   *     indexName.
   */
  public FieldMappers indexName(String indexName) {
    return fieldMappers.indexName(indexName);
  }

  /**
   * Returns the {@link FieldMappers} of all the {@link FieldMapper}s that are registered under the
   * give fullName across all the different {@link DocumentMapper} types.
   *
   * @param fullName The full name
   * @return All teh {@link FieldMappers} across all the {@link DocumentMapper}s for the given
   *     fullName.
   */
  public FieldMappers fullName(String fullName) {
    return fieldMappers.fullName(fullName);
  }

  /** Returns objects mappers based on the full path of the object. */
  public ObjectMappers objectMapper(String path) {
    return fullPathObjectMappers.get(path);
  }

  /**
   * Returns all the fields that match the given pattern, with an optional narrowing based on a list
   * of types.
   */
  public Set<String> simpleMatchToIndexNames(String pattern, @Nullable String[] types) {
    if (types == null || types.length == 0) {
      return simpleMatchToIndexNames(pattern);
    }
    if (types.length == 1 && types[0].equals("_all")) {
      return simpleMatchToIndexNames(pattern);
    }
    if (!Regex.isSimpleMatchPattern(pattern)) {
      return ImmutableSet.of(pattern);
    }
    Set<String> fields = Sets.newHashSet();
    for (String type : types) {
      DocumentMapper possibleDocMapper = mappers.get(type);
      if (possibleDocMapper != null) {
        for (String indexName : possibleDocMapper.mappers().simpleMatchToIndexNames(pattern)) {
          fields.add(indexName);
        }
      }
    }
    return fields;
  }

  /**
   * Returns all the fields that match the given pattern. If the pattern is prefixed with a type
   * then the fields will be returned with a type prefix.
   */
  public Set<String> simpleMatchToIndexNames(String pattern) {
    if (!Regex.isSimpleMatchPattern(pattern)) {
      return ImmutableSet.of(pattern);
    }
    int dotIndex = pattern.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = pattern.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        Set<String> typedFields = Sets.newHashSet();
        for (String indexName : possibleDocMapper.mappers().simpleMatchToIndexNames(pattern)) {
          typedFields.add(possibleType + "." + indexName);
        }
        return typedFields;
      }
    }
    return fieldMappers.simpleMatchToIndexNames(pattern);
  }

  public SmartNameObjectMapper smartNameObjectMapper(String smartName, @Nullable String[] types) {
    if (types == null || types.length == 0) {
      return smartNameObjectMapper(smartName);
    }
    if (types.length == 1 && types[0].equals("_all")) {
      return smartNameObjectMapper(smartName);
    }
    for (String type : types) {
      DocumentMapper possibleDocMapper = mappers.get(type);
      if (possibleDocMapper != null) {
        ObjectMapper mapper = possibleDocMapper.objectMappers().get(smartName);
        if (mapper != null) {
          return new SmartNameObjectMapper(mapper, possibleDocMapper);
        }
      }
    }
    // did not find one, see if its prefixed by type
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possiblePath = smartName.substring(dotIndex + 1);
        ObjectMapper mapper = possibleDocMapper.objectMappers().get(possiblePath);
        if (mapper != null) {
          return new SmartNameObjectMapper(mapper, possibleDocMapper);
        }
      }
    }
    // did not explicitly find one under the types provided, or prefixed by type...
    return null;
  }

  public SmartNameObjectMapper smartNameObjectMapper(String smartName) {
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possiblePath = smartName.substring(dotIndex + 1);
        ObjectMapper mapper = possibleDocMapper.objectMappers().get(possiblePath);
        if (mapper != null) {
          return new SmartNameObjectMapper(mapper, possibleDocMapper);
        }
      }
    }
    ObjectMappers mappers = objectMapper(smartName);
    if (mappers != null) {
      return new SmartNameObjectMapper(mappers.mapper(), null);
    }
    return null;
  }

  /**
   * Same as {@link #smartNameFieldMappers(String)} but returns the first field mapper for it.
   * Returns <tt>null</tt> if there is none.
   */
  public FieldMapper smartNameFieldMapper(String smartName) {
    FieldMappers fieldMappers = smartNameFieldMappers(smartName);
    if (fieldMappers != null) {
      return fieldMappers.mapper();
    }
    return null;
  }

  public FieldMapper smartNameFieldMapper(String smartName, @Nullable String[] types) {
    FieldMappers fieldMappers = smartNameFieldMappers(smartName, types);
    if (fieldMappers != null) {
      return fieldMappers.mapper();
    }
    return null;
  }

  public FieldMappers smartNameFieldMappers(String smartName, @Nullable String[] types) {
    if (types == null || types.length == 0) {
      return smartNameFieldMappers(smartName);
    }
    for (String type : types) {
      DocumentMapper documentMapper = mappers.get(type);
      // we found a mapper
      if (documentMapper != null) {
        // see if we find a field for it
        FieldMappers mappers = documentMapper.mappers().smartName(smartName);
        if (mappers != null) {
          return mappers;
        }
      }
    }
    // did not find explicit field in the type provided, see if its prefixed with type
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possibleName = smartName.substring(dotIndex + 1);
        FieldMappers mappers = possibleDocMapper.mappers().smartName(possibleName);
        if (mappers != null) {
          return mappers;
        }
      }
    }
    // we did not find the field mapping in any of the types, so don't go and try to find
    // it in other types...
    return null;
  }

  /** Same as {@link #smartName(String)}, except it returns just the field mappers. */
  public FieldMappers smartNameFieldMappers(String smartName) {
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possibleName = smartName.substring(dotIndex + 1);
        FieldMappers mappers = possibleDocMapper.mappers().smartName(possibleName);
        if (mappers != null) {
          return mappers;
        }
      }
    }
    FieldMappers mappers = fullName(smartName);
    if (mappers != null) {
      return mappers;
    }
    mappers = indexName(smartName);
    if (mappers != null) {
      return mappers;
    }
    return name(smartName);
  }

  public SmartNameFieldMappers smartName(String smartName, @Nullable String[] types) {
    if (types == null || types.length == 0) {
      return smartName(smartName);
    }
    if (types.length == 1 && types[0].equals("_all")) {
      return smartName(smartName);
    }
    for (String type : types) {
      DocumentMapper documentMapper = mappers.get(type);
      // we found a mapper
      if (documentMapper != null) {
        // see if we find a field for it
        FieldMappers mappers = documentMapper.mappers().smartName(smartName);
        if (mappers != null) {
          return new SmartNameFieldMappers(this, mappers, documentMapper, false);
        }
      }
    }
    // did not find explicit field in the type provided, see if its prefixed with type
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possibleName = smartName.substring(dotIndex + 1);
        FieldMappers mappers = possibleDocMapper.mappers().smartName(possibleName);
        if (mappers != null) {
          return new SmartNameFieldMappers(this, mappers, possibleDocMapper, true);
        }
      }
    }
    // we did not find the field mapping in any of the types, so don't go and try to find
    // it in other types...
    return null;
  }

  /**
   * Returns smart field mappers based on a smart name. A smart name is one that can optioannly be
   * prefixed with a type (and then a '.'). If it is, then the {@link
   * MapperService.SmartNameFieldMappers} will have the doc mapper set.
   *
   * <p>
   *
   * <p>It also (without the optional type prefix) try and find the {@link FieldMappers} for the
   * specific name. It will first try to find it based on the full name (with the dots if its a
   * compound name). If it is not found, will try and find it based on the indexName (which can be
   * controlled in the mapping), and last, will try it based no the name itself.
   *
   * <p>
   *
   * <p>If nothing is found, returns null.
   */
  public SmartNameFieldMappers smartName(String smartName) {
    int dotIndex = smartName.indexOf('.');
    if (dotIndex != -1) {
      String possibleType = smartName.substring(0, dotIndex);
      DocumentMapper possibleDocMapper = mappers.get(possibleType);
      if (possibleDocMapper != null) {
        String possibleName = smartName.substring(dotIndex + 1);
        FieldMappers mappers = possibleDocMapper.mappers().smartName(possibleName);
        if (mappers != null) {
          return new SmartNameFieldMappers(this, mappers, possibleDocMapper, true);
        }
      }
    }
    FieldMappers fieldMappers = fullName(smartName);
    if (fieldMappers != null) {
      return new SmartNameFieldMappers(this, fieldMappers, null, false);
    }
    fieldMappers = indexName(smartName);
    if (fieldMappers != null) {
      return new SmartNameFieldMappers(this, fieldMappers, null, false);
    }
    fieldMappers = name(smartName);
    if (fieldMappers != null) {
      return new SmartNameFieldMappers(this, fieldMappers, null, false);
    }
    return null;
  }

  public Analyzer searchAnalyzer() {
    return this.searchAnalyzer;
  }

  public Analyzer searchQuoteAnalyzer() {
    return this.searchQuoteAnalyzer;
  }

  public Analyzer fieldSearchAnalyzer(String field) {
    return this.searchAnalyzer.getWrappedAnalyzer(field);
  }

  public Analyzer fieldSearchQuoteAnalyzer(String field) {
    return this.searchQuoteAnalyzer.getWrappedAnalyzer(field);
  }

  /** Resolves the closest inherited {@link ObjectMapper} that is nested. */
  public ObjectMapper resolveClosestNestedObjectMapper(String fieldName) {
    int indexOf = fieldName.lastIndexOf('.');
    if (indexOf == -1) {
      return null;
    } else {
      do {
        String objectPath = fieldName.substring(0, indexOf);
        ObjectMappers objectMappers = objectMapper(objectPath);
        if (objectMappers == null) {
          return null;
        }

        if (objectMappers.hasNested()) {
          for (ObjectMapper objectMapper : objectMappers) {
            if (objectMapper.nested().isNested()) {
              return objectMapper;
            }
          }
        }

        indexOf = objectPath.lastIndexOf('.');
      } while (indexOf != -1);
    }

    return null;
  }

  public static class SmartNameObjectMapper {
    private final ObjectMapper mapper;
    private final DocumentMapper docMapper;

    public SmartNameObjectMapper(ObjectMapper mapper, @Nullable DocumentMapper docMapper) {
      this.mapper = mapper;
      this.docMapper = docMapper;
    }

    public boolean hasMapper() {
      return mapper != null;
    }

    public ObjectMapper mapper() {
      return mapper;
    }

    public boolean hasDocMapper() {
      return docMapper != null;
    }

    public DocumentMapper docMapper() {
      return docMapper;
    }
  }

  public static class SmartNameFieldMappers {
    private final MapperService mapperService;
    private final FieldMappers fieldMappers;
    private final DocumentMapper docMapper;
    private final boolean explicitTypeInName;

    public SmartNameFieldMappers(
        MapperService mapperService,
        FieldMappers fieldMappers,
        @Nullable DocumentMapper docMapper,
        boolean explicitTypeInName) {
      this.mapperService = mapperService;
      this.fieldMappers = fieldMappers;
      this.docMapper = docMapper;
      this.explicitTypeInName = explicitTypeInName;
    }

    /** Has at least one mapper for the field. */
    public boolean hasMapper() {
      return !fieldMappers.isEmpty();
    }

    /** The first mapper for the smart named field. */
    public FieldMapper mapper() {
      return fieldMappers.mapper();
    }

    /** All the field mappers for the smart name field. */
    public FieldMappers fieldMappers() {
      return fieldMappers;
    }

    /**
     * If the smart name was a typed field, with a type that we resolved, will return <tt>true</tt>.
     */
    public boolean hasDocMapper() {
      return docMapper != null;
    }

    /**
     * If the smart name was a typed field, with a type that we resolved, will return the document
     * mapper for it.
     */
    public DocumentMapper docMapper() {
      return docMapper;
    }

    /** Returns <tt>true</tt> if the type is explicitly specified in the name. */
    public boolean explicitTypeInName() {
      return this.explicitTypeInName;
    }

    public boolean explicitTypeInNameWithDocMapper() {
      return explicitTypeInName && docMapper != null;
    }

    /** The best effort search analyzer associated with this field. */
    public Analyzer searchAnalyzer() {
      if (hasMapper()) {
        Analyzer analyzer = mapper().searchAnalyzer();
        if (analyzer != null) {
          return analyzer;
        }
      }
      if (docMapper != null && docMapper.searchAnalyzer() != null) {
        return docMapper.searchAnalyzer();
      }
      return mapperService.searchAnalyzer();
    }

    public Analyzer searchQuoteAnalyzer() {
      if (hasMapper()) {
        Analyzer analyzer = mapper().searchQuoteAnalyzer();
        if (analyzer != null) {
          return analyzer;
        }
      }
      if (docMapper != null && docMapper.searchQuotedAnalyzer() != null) {
        return docMapper.searchQuotedAnalyzer();
      }
      return mapperService.searchQuoteAnalyzer();
    }
  }

  final class SmartIndexNameSearchAnalyzer extends AnalyzerWrapper {

    private final Analyzer defaultAnalyzer;

    SmartIndexNameSearchAnalyzer(Analyzer defaultAnalyzer) {
      this.defaultAnalyzer = defaultAnalyzer;
    }

    @Override
    protected Analyzer getWrappedAnalyzer(String fieldName) {
      int dotIndex = fieldName.indexOf('.');
      if (dotIndex != -1) {
        String possibleType = fieldName.substring(0, dotIndex);
        DocumentMapper possibleDocMapper = mappers.get(possibleType);
        if (possibleDocMapper != null) {
          return possibleDocMapper.mappers().searchAnalyzer();
        }
      }
      FieldMappers mappers = fieldMappers.fullName(fieldName);
      if (mappers != null
          && mappers.mapper() != null
          && mappers.mapper().searchAnalyzer() != null) {
        return mappers.mapper().searchAnalyzer();
      }

      mappers = fieldMappers.indexName(fieldName);
      if (mappers != null
          && mappers.mapper() != null
          && mappers.mapper().searchAnalyzer() != null) {
        return mappers.mapper().searchAnalyzer();
      }
      return defaultAnalyzer;
    }

    @Override
    protected TokenStreamComponents wrapComponents(
        String fieldName, TokenStreamComponents components) {
      return components;
    }
  }

  final class SmartIndexNameSearchQuoteAnalyzer extends AnalyzerWrapper {

    private final Analyzer defaultAnalyzer;

    SmartIndexNameSearchQuoteAnalyzer(Analyzer defaultAnalyzer) {
      this.defaultAnalyzer = defaultAnalyzer;
    }

    @Override
    protected Analyzer getWrappedAnalyzer(String fieldName) {
      int dotIndex = fieldName.indexOf('.');
      if (dotIndex != -1) {
        String possibleType = fieldName.substring(0, dotIndex);
        DocumentMapper possibleDocMapper = mappers.get(possibleType);
        if (possibleDocMapper != null) {
          return possibleDocMapper.mappers().searchQuoteAnalyzer();
        }
      }
      FieldMappers mappers = fieldMappers.fullName(fieldName);
      if (mappers != null
          && mappers.mapper() != null
          && mappers.mapper().searchQuoteAnalyzer() != null) {
        return mappers.mapper().searchQuoteAnalyzer();
      }

      mappers = fieldMappers.indexName(fieldName);
      if (mappers != null
          && mappers.mapper() != null
          && mappers.mapper().searchQuoteAnalyzer() != null) {
        return mappers.mapper().searchQuoteAnalyzer();
      }
      return defaultAnalyzer;
    }

    @Override
    protected TokenStreamComponents wrapComponents(
        String fieldName, TokenStreamComponents components) {
      return components;
    }
  }

  class InternalFieldMapperListener extends FieldMapperListener {
    @Override
    public void fieldMapper(FieldMapper fieldMapper) {
      addFieldMappers(new FieldMapper[] {fieldMapper});
    }

    @Override
    public void fieldMappers(FieldMapper... fieldMappers) {
      addFieldMappers(fieldMappers);
    }
  }

  class InternalObjectMapperListener extends ObjectMapperListener {
    @Override
    public void objectMapper(ObjectMapper objectMapper) {
      addObjectMappers(new ObjectMapper[] {objectMapper});
    }

    @Override
    public void objectMappers(ObjectMapper... objectMappers) {
      addObjectMappers(objectMappers);
    }
  }
}
Example #12
0
 public MutationState dropTable(DropTableStatement statement) throws SQLException {
   connection.rollback();
   boolean wasAutoCommit = connection.getAutoCommit();
   try {
     TableName tableNameNode = statement.getTableName();
     String schemaName = tableNameNode.getSchemaName();
     String tableName = tableNameNode.getTableName();
     byte[] key = SchemaUtil.getTableKey(schemaName, tableName);
     Long scn = connection.getSCN();
     @SuppressWarnings(
         "deprecation") // FIXME: Remove when unintentionally deprecated method is fixed
                        // (HBASE-7870).
     // FIXME: the version of the Delete constructor without the lock args was introduced
     // in 0.94.4, thus if we try to use it here we can no longer use the 0.94.2 version
     // of the client.
     List<Mutation> tableMetaData =
         Collections.<Mutation>singletonList(
             new Delete(key, scn == null ? HConstants.LATEST_TIMESTAMP : scn, null));
     MetaDataMutationResult result =
         connection.getQueryServices().dropTable(tableMetaData, statement.isView());
     MutationCode code = result.getMutationCode();
     switch (code) {
       case TABLE_NOT_FOUND:
         if (!statement.ifExists()) {
           throw new TableNotFoundException(schemaName, tableName);
         }
         break;
       case NEWER_TABLE_FOUND:
         throw new NewerTableAlreadyExistsException(schemaName, tableName);
       case UNALLOWED_TABLE_MUTATION:
         throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
             .setSchemaName(schemaName)
             .setTableName(tableName)
             .build()
             .buildException();
       default:
         try {
           connection.removeTable(schemaName, tableName);
         } catch (TableNotFoundException e) { // Ignore - just means wasn't cached
         }
         if (!statement.isView()) {
           connection.setAutoCommit(true);
           // Delete everything in the column. You'll still be able to do queries at earlier
           // timestamps
           long ts = (scn == null ? result.getMutationTime() : scn);
           // Create empty table and schema - they're only used to get the name from
           // PName name, PTableType type, long timeStamp, long sequenceNumber, List<PColumn>
           // columns
           PTable table = result.getTable();
           PSchema schema =
               new PSchemaImpl(
                   schemaName,
                   ImmutableMap.<String, PTable>of(table.getName().getString(), table));
           TableRef tableRef = new TableRef(null, table, schema, ts);
           MutationPlan plan =
               new PostDDLCompiler(connection)
                   .compile(tableRef, null, Collections.<PColumn>emptyList(), ts);
           return connection.getQueryServices().updateData(plan);
         }
         break;
     }
     return new MutationState(0, connection);
   } finally {
     connection.setAutoCommit(wasAutoCommit);
   }
 }
Example #13
0
  public MutationState createTable(CreateTableStatement statement, byte[][] splits)
      throws SQLException {
    PTableType tableType = statement.getTableType();
    boolean isView = tableType == PTableType.VIEW;
    if (isView && !statement.getProps().isEmpty()) {
      throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_TABLE_CONFIG)
          .build()
          .buildException();
    }
    connection.rollback();
    boolean wasAutoCommit = connection.getAutoCommit();
    try {
      connection.setAutoCommit(false);
      TableName tableNameNode = statement.getTableName();
      String schemaName = tableNameNode.getSchemaName();
      String tableName = tableNameNode.getTableName();

      PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint();
      String pkName = null;
      Set<String> pkColumns = Collections.<String>emptySet();
      Iterator<String> pkColumnsIterator = Iterators.emptyIterator();
      if (pkConstraint != null) {
        pkColumns = pkConstraint.getColumnNames();
        pkColumnsIterator = pkColumns.iterator();
        pkName = pkConstraint.getName();
      }

      List<ColumnDef> colDefs = statement.getColumnDefs();
      List<PColumn> columns = Lists.newArrayListWithExpectedSize(colDefs.size());
      PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN);
      int columnOrdinal = 0;
      Map<String, PName> familyNames = Maps.newLinkedHashMap();
      boolean isPK = false;
      for (ColumnDef colDef : colDefs) {
        if (colDef.isPK()) {
          if (isPK) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS)
                .setColumnName(colDef.getColumnDefName().getColumnName().getName())
                .build()
                .buildException();
          }
          isPK = true;
        }
        PColumn column = newColumn(columnOrdinal++, colDef, pkConstraint);
        if (SchemaUtil.isPKColumn(column)) {
          // TODO: remove this constraint?
          if (!pkColumns.isEmpty()
              && !column.getName().getString().equals(pkColumnsIterator.next())) {
            throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_OUT_OF_ORDER)
                .setSchemaName(schemaName)
                .setTableName(tableName)
                .setColumnName(column.getName().getString())
                .build()
                .buildException();
          }
        }
        columns.add(column);
        if (colDef.getDataType() == PDataType.BINARY && colDefs.size() > 1) {
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.BINARY_IN_ROW_KEY)
              .setSchemaName(schemaName)
              .setTableName(tableName)
              .setColumnName(column.getName().getString())
              .build()
              .buildException();
        }
        if (column.getFamilyName() != null) {
          familyNames.put(column.getFamilyName().getString(), column.getFamilyName());
        }
      }
      if (!isPK && pkColumns.isEmpty()) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING)
            .setSchemaName(schemaName)
            .setTableName(tableName)
            .build()
            .buildException();
      }

      List<Pair<byte[], Map<String, Object>>> familyPropList =
          Lists.newArrayListWithExpectedSize(familyNames.size());
      Map<String, Object> commonFamilyProps = Collections.emptyMap();
      Map<String, Object> tableProps = Collections.emptyMap();
      if (!statement.getProps().isEmpty()) {
        if (statement.isView()) {
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES)
              .build()
              .buildException();
        }
        for (String familyName : statement.getProps().keySet()) {
          if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) {
            if (familyNames.get(familyName) == null) {
              throw new SQLExceptionInfo.Builder(SQLExceptionCode.PROPERTIES_FOR_FAMILY)
                  .setFamilyName(familyName)
                  .build()
                  .buildException();
            }
          }
        }
        commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());
        tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size());

        Collection<Pair<String, Object>> props =
            statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY);
        // Somewhat hacky way of determining if property is for HColumnDescriptor or
        // HTableDescriptor
        HColumnDescriptor defaultDescriptor =
            new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES);
        for (Pair<String, Object> prop : props) {
          if (defaultDescriptor.getValue(prop.getFirst()) != null) {
            commonFamilyProps.put(prop.getFirst(), prop.getSecond());
          } else {
            tableProps.put(prop.getFirst(), prop.getSecond());
          }
        }
      }

      for (PName familyName : familyNames.values()) {
        Collection<Pair<String, Object>> props = statement.getProps().get(familyName.getString());
        if (props.isEmpty()) {
          familyPropList.add(
              new Pair<byte[], Map<String, Object>>(familyName.getBytes(), commonFamilyProps));
        } else {
          Map<String, Object> combinedFamilyProps =
              Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size());
          combinedFamilyProps.putAll(commonFamilyProps);
          for (Pair<String, Object> prop : props) {
            combinedFamilyProps.put(prop.getFirst(), prop.getSecond());
          }
          familyPropList.add(
              new Pair<byte[], Map<String, Object>>(familyName.getBytes(), combinedFamilyProps));
        }
      }

      // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists
      if (tableType == PTableType.SYSTEM) {
        PTable table =
            new PTableImpl(
                new PNameImpl(tableName),
                tableType,
                MetaDataProtocol.MIN_TABLE_TIMESTAMP,
                0,
                QueryConstants.SYSTEM_TABLE_PK_NAME,
                null,
                columns);
        connection.addTable(schemaName, table);
      }

      for (PColumn column : columns) {
        addColumnMutation(schemaName, tableName, column, colUpsert);
      }

      Integer saltBucketNum = (Integer) tableProps.remove(PhoenixDatabaseMetaData.SALT_BUCKETS);
      if (saltBucketNum != null
          && (saltBucketNum <= 0 || saltBucketNum > SaltingUtil.MAX_BUCKET_NUM)) {
        throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM)
            .build()
            .buildException();
      }

      PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE);
      tableUpsert.setString(1, schemaName);
      tableUpsert.setString(2, tableName);
      tableUpsert.setString(3, tableType.getSerializedValue());
      tableUpsert.setInt(4, 0);
      tableUpsert.setInt(5, columnOrdinal);
      if (saltBucketNum != null) {
        tableUpsert.setInt(6, saltBucketNum);
      } else {
        tableUpsert.setNull(6, Types.INTEGER);
      }
      tableUpsert.setString(7, pkName);
      tableUpsert.execute();

      final List<Mutation> tableMetaData = connection.getMutationState().toMutations();
      connection.rollback();

      MetaDataMutationResult result =
          connection
              .getQueryServices()
              .createTable(tableMetaData, isView, tableProps, familyPropList, splits);
      MutationCode code = result.getMutationCode();
      switch (code) {
        case TABLE_ALREADY_EXISTS:
          connection.addTable(schemaName, result.getTable());
          if (!statement.ifNotExists()) {
            throw new TableAlreadyExistsException(schemaName, tableName);
          }
          break;
        case NEWER_TABLE_FOUND:
          // TODO: add table if in result?
          throw new NewerTableAlreadyExistsException(schemaName, tableName);
        case UNALLOWED_TABLE_MUTATION:
          throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE)
              .setSchemaName(schemaName)
              .setTableName(tableName)
              .build()
              .buildException();
        default:
          PTable table =
              new PTableImpl(
                  new PNameImpl(tableName),
                  tableType,
                  result.getMutationTime(),
                  0,
                  pkName,
                  saltBucketNum,
                  columns);
          connection.addTable(schemaName, table);
          if (tableType == PTableType.USER) {
            connection.setAutoCommit(true);
            // Delete everything in the column. You'll still be able to do queries at earlier
            // timestamps
            Long scn = connection.getSCN();
            long ts = (scn == null ? result.getMutationTime() : scn);
            PSchema schema =
                new PSchemaImpl(
                    schemaName,
                    ImmutableMap.<String, PTable>of(table.getName().getString(), table));
            TableRef tableRef = new TableRef(null, table, schema, ts);
            byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table.getColumnFamilies());
            MutationPlan plan =
                new PostDDLCompiler(connection).compile(tableRef, emptyCF, null, ts);
            return connection.getQueryServices().updateData(plan);
          }
          break;
      }
      return new MutationState(0, connection);
    } finally {
      connection.setAutoCommit(wasAutoCommit);
    }
  }
 private MapMessage createPayload(
     String key1, Object value1, String key2, Object value2, String key3, Object value3)
     throws JMSException {
   return newMapMessage(ImmutableMap.<String, Object>of(key1, value1, key2, value2, key3, value3));
 }
 private MapMessage createPayload(String key, Object value) throws JMSException {
   return newMapMessage(ImmutableMap.<String, Object>of(key, value));
 }
public class ManagedProxyClassGenerator extends AbstractProxyClassGenerator {
  /*
     Note: there is deliberately no internal synchronizing or caching at this level.
     Class generation should be performed behind a ManagedProxyFactory.
  */

  private static final String STATE_FIELD_NAME = "$state";
  private static final String TYPE_CONVERTER_FIELD_NAME = "$typeConverter";
  private static final String MANAGED_TYPE_FIELD_NAME = "$managedType";
  private static final String DELEGATE_FIELD_NAME = "$delegate";
  private static final String CAN_CALL_SETTERS_FIELD_NAME = "$canCallSetters";
  private static final Type OBJECT_TYPE = Type.getType(Object.class);
  private static final Type STRING_TYPE = Type.getType(String.class);
  private static final Type CLASS_TYPE = Type.getType(Class.class);
  private static final Type CLOSURE_TYPE = Type.getType(Closure.class);
  private static final Type TYPE_CONVERTER_TYPE = Type.getType(TypeConverter.class);
  private static final Type MODELTYPE_TYPE = Type.getType(ModelType.class);
  private static final Type MODEL_ELEMENT_STATE_TYPE = Type.getType(ModelElementState.class);
  private static final String STATE_SET_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, OBJECT_TYPE);
  private static final String STATE_GET_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE);
  private static final String STATE_APPLY_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, CLOSURE_TYPE);
  private static final String MANAGED_INSTANCE_TYPE = Type.getInternalName(ManagedInstance.class);
  private static final String NO_DELEGATE_CONSTRUCTOR_DESCRIPTOR =
      Type.getMethodDescriptor(Type.VOID_TYPE, MODEL_ELEMENT_STATE_TYPE, TYPE_CONVERTER_TYPE);
  private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE);
  private static final String MUTABLE_MODEL_NODE_TYPE =
      Type.getInternalName(MutableModelNode.class);
  private static final String GET_BACKING_NODE_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(Type.getType(MutableModelNode.class));
  private static final String MODELTYPE_INTERNAL_NAME = MODELTYPE_TYPE.getInternalName();
  private static final String MODELTYPE_OF_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(MODELTYPE_TYPE, CLASS_TYPE);
  private static final String GET_MANAGED_TYPE_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(MODELTYPE_TYPE);
  private static final String GET_PROPERTY_MISSING_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE);
  private static final String MISSING_PROPERTY_EXCEPTION_TYPE =
      Type.getInternalName(MissingPropertyException.class);
  private static final String CLASS_INTERNAL_NAME = Type.getInternalName(Class.class);
  private static final String FOR_NAME_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(CLASS_TYPE, STRING_TYPE);
  private static final String HASH_CODE_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(Type.getType(int.class));
  private static final String EQUALS_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(Type.getType(boolean.class), OBJECT_TYPE);
  private static final String OBJECT_ARRAY_TYPE = Type.getInternalName(Object[].class);
  private static final String MISSING_METHOD_EXCEPTION_TYPE =
      Type.getInternalName(MissingMethodException.class);
  private static final String MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR =
      Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE, CLASS_TYPE);
  private static final String METHOD_MISSING_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE, OBJECT_TYPE);
  private static final String SET_PROPERTY_MISSING_METHOD_DESCRIPTOR =
      Type.getMethodDescriptor(OBJECT_TYPE, STRING_TYPE, OBJECT_TYPE);
  private static final String MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR =
      Type.getMethodDescriptor(
          Type.VOID_TYPE, STRING_TYPE, CLASS_TYPE, Type.getType(Object[].class));
  private static final Equivalence<Method> METHOD_EQUIVALENCE = new MethodSignatureEquivalence();
  private static final String SET_OBJECT_PROPERTY_DESCRIPTOR =
      Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE);
  private static final String COERCE_TO_SCALAR_DESCRIPTOR =
      Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE, CLASS_TYPE, Type.getType(boolean.class));
  private static final String MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME =
      MODEL_ELEMENT_STATE_TYPE.getInternalName();
  private static final Map<Class<?>, Class<?>> BOXED_TYPES =
      ImmutableMap.<Class<?>, Class<?>>builder()
          .put(byte.class, Byte.class)
          .put(short.class, Short.class)
          .put(int.class, Integer.class)
          .put(boolean.class, Boolean.class)
          .put(float.class, Float.class)
          .put(char.class, Character.class)
          .put(double.class, Double.class)
          .put(long.class, Long.class)
          .build();

  /**
   * Generates an implementation of the given managed type.
   *
   * <p>The generated class will implement/extend the managed type and will:
   *
   * <ul>
   *   <li>provide implementations for abstract getters and setters that delegate to model nodes
   *   <li>provide a `toString()` implementation
   *   <li>mix-in implementation of {@link ManagedInstance}
   *   <li>provide a constructor that accepts a {@link ModelElementState}, which will be used to
   *       implement the above.
   * </ul>
   *
   * In case a delegate schema is supplied, the generated class will also have:
   *
   * <ul>
   *   <li>a constructor that also takes a delegate instance
   *   <li>methods that call through to the delegate instance
   * </ul>
   */
  public <T, M extends T, D extends T> Class<? extends M> generate(
      StructSchema<M> viewSchema, @Nullable StructSchema<D> delegateSchema) {
    if (delegateSchema != null
        && Modifier.isAbstract(delegateSchema.getType().getConcreteClass().getModifiers())) {
      throw new IllegalArgumentException("Delegate type must be null or a non-abstract type");
    }
    ClassWriter visitor = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);

    ModelType<M> viewType = viewSchema.getType();

    StringBuilder generatedTypeNameBuilder = new StringBuilder(viewType.getName());
    if (delegateSchema != null) {
      generatedTypeNameBuilder
          .append("$BackedBy_")
          .append(delegateSchema.getType().getName().replaceAll("\\.", "_"));
    } else {
      generatedTypeNameBuilder.append("$Impl");
    }

    String generatedTypeName = generatedTypeNameBuilder.toString();
    Type generatedType = Type.getType("L" + generatedTypeName.replaceAll("\\.", "/") + ";");

    Class<M> viewClass = viewType.getConcreteClass();
    Class<?> superclass;
    final ImmutableSet.Builder<String> interfacesToImplement = ImmutableSet.builder();
    final ImmutableSet.Builder<Class<?>> typesToDelegate = ImmutableSet.builder();
    typesToDelegate.add(viewClass);
    interfacesToImplement.add(MANAGED_INSTANCE_TYPE);
    if (viewClass.isInterface()) {
      superclass = Object.class;
      interfacesToImplement.add(Type.getInternalName(viewClass));
    } else {
      superclass = viewClass;
    }
    // TODO:LPTR This should be removed once BinaryContainer is a ModelMap
    // We need to also implement all the interfaces of the delegate type because otherwise
    // BinaryContainer won't recognize managed binaries as BinarySpecInternal
    if (delegateSchema != null) {
      ModelSchemaUtils.walkTypeHierarchy(
          delegateSchema.getType().getConcreteClass(),
          new ModelSchemaUtils.TypeVisitor<D>() {
            @Override
            public void visitType(Class<? super D> type) {
              if (type.isInterface()) {
                typesToDelegate.add(type);
                interfacesToImplement.add(Type.getInternalName(type));
              }
            }
          });
    }

    generateProxyClass(
        visitor,
        viewSchema,
        delegateSchema,
        interfacesToImplement.build(),
        typesToDelegate.build(),
        generatedType,
        Type.getType(superclass));

    ClassLoader targetClassLoader = viewClass.getClassLoader();
    if (delegateSchema != null) {
      // TODO - remove this once the above is removed
      try {
        viewClass.getClassLoader().loadClass(delegateSchema.getType().getConcreteClass().getName());
      } catch (ClassNotFoundException e) {
        // Delegate class is not visible to managed view type -> view type is more general than
        // delegate type, so use the delegate classloader instead
        targetClassLoader = delegateSchema.getType().getConcreteClass().getClassLoader();
      }
    }

    return defineClass(visitor, targetClassLoader, generatedTypeName);
  }

  private void generateProxyClass(
      ClassWriter visitor,
      StructSchema<?> viewSchema,
      StructSchema<?> delegateSchema,
      Collection<String> interfacesToImplement,
      Set<Class<?>> typesToDelegate,
      Type generatedType,
      Type superclassType) {
    ModelType<?> viewType = viewSchema.getType();
    Class<?> viewClass = viewType.getConcreteClass();
    declareClass(visitor, interfacesToImplement, generatedType, superclassType);
    declareStateField(visitor);
    declareTypeConverterField(visitor);
    declareManagedTypeField(visitor);
    declareCanCallSettersField(visitor);
    writeStaticConstructor(visitor, generatedType, viewClass);
    writeConstructor(visitor, generatedType, superclassType, delegateSchema);
    writeToString(visitor, generatedType, viewClass, delegateSchema);
    writeManagedInstanceMethods(visitor, generatedType);
    if (delegateSchema != null) {
      declareDelegateField(visitor, delegateSchema);
      writeDelegateMethods(visitor, generatedType, delegateSchema, typesToDelegate);
    }
    writeGroovyMethods(visitor, viewClass);
    writePropertyMethods(visitor, generatedType, viewSchema, delegateSchema);
    writeHashCodeMethod(visitor, generatedType);
    writeEqualsMethod(visitor, generatedType);
    visitor.visitEnd();
  }

  private void declareClass(
      ClassVisitor visitor,
      Collection<String> interfaceInternalNames,
      Type generatedType,
      Type superclassType) {
    visitor.visit(
        V1_6,
        ACC_PUBLIC,
        generatedType.getInternalName(),
        null,
        superclassType.getInternalName(),
        Iterables.toArray(interfaceInternalNames, String.class));
  }

  private void declareStateField(ClassVisitor visitor) {
    declareField(visitor, STATE_FIELD_NAME, ModelElementState.class);
  }

  private void declareTypeConverterField(ClassVisitor visitor) {
    declareField(visitor, TYPE_CONVERTER_FIELD_NAME, TypeConverter.class);
  }

  private void declareManagedTypeField(ClassVisitor visitor) {
    declareStaticField(visitor, MANAGED_TYPE_FIELD_NAME, ModelType.class);
  }

  private void declareDelegateField(ClassVisitor visitor, StructSchema<?> delegateSchema) {
    declareField(visitor, DELEGATE_FIELD_NAME, delegateSchema.getType().getConcreteClass());
  }

  private void declareCanCallSettersField(ClassVisitor visitor) {
    declareField(visitor, CAN_CALL_SETTERS_FIELD_NAME, Boolean.TYPE);
  }

  private void declareField(ClassVisitor visitor, String name, Class<?> fieldClass) {
    visitor.visitField(
        ACC_PRIVATE | ACC_FINAL | ACC_SYNTHETIC, name, Type.getDescriptor(fieldClass), null, null);
  }

  private FieldVisitor declareStaticField(ClassVisitor visitor, String name, Class<?> fieldClass) {
    return visitor.visitField(
        ACC_PRIVATE | ACC_FINAL | ACC_STATIC | ACC_SYNTHETIC,
        name,
        Type.getDescriptor(fieldClass),
        null,
        null);
  }

  private void writeConstructor(
      ClassVisitor visitor,
      Type generatedType,
      Type superclassType,
      StructSchema<?> delegateSchema) {
    String constructorDescriptor;
    Type delegateType;
    if (delegateSchema == null) {
      delegateType = null;
      constructorDescriptor = NO_DELEGATE_CONSTRUCTOR_DESCRIPTOR;
    } else {
      delegateType = Type.getType(delegateSchema.getType().getConcreteClass());
      constructorDescriptor =
          Type.getMethodDescriptor(
              Type.VOID_TYPE, MODEL_ELEMENT_STATE_TYPE, TYPE_CONVERTER_TYPE, delegateType);
    }
    MethodVisitor constructorVisitor =
        declareMethod(visitor, CONSTRUCTOR_NAME, constructorDescriptor, CONCRETE_SIGNATURE);

    invokeSuperConstructor(constructorVisitor, superclassType);
    assignStateField(constructorVisitor, generatedType);
    assignTypeConverterField(constructorVisitor, generatedType);
    if (delegateType != null) {
      assignDelegateField(constructorVisitor, generatedType, delegateType);
    }
    setCanCallSettersField(constructorVisitor, generatedType, true);
    finishVisitingMethod(constructorVisitor);
  }

  private void writeStaticConstructor(
      ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass) {
    MethodVisitor constructorVisitor =
        declareMethod(visitor, STATIC_CONSTRUCTOR_NAME, "()V", CONCRETE_SIGNATURE, ACC_STATIC);
    writeManagedTypeStaticField(generatedType, managedTypeClass, constructorVisitor);
    finishVisitingMethod(constructorVisitor);
  }

  private void writeManagedTypeStaticField(
      Type generatedType, Class<?> managedTypeClass, MethodVisitor constructorVisitor) {
    constructorVisitor.visitLdcInsn(Type.getType(managedTypeClass));
    constructorVisitor.visitMethodInsn(
        INVOKESTATIC, MODELTYPE_INTERNAL_NAME, "of", MODELTYPE_OF_METHOD_DESCRIPTOR, false);
    constructorVisitor.visitFieldInsn(
        PUTSTATIC,
        generatedType.getInternalName(),
        MANAGED_TYPE_FIELD_NAME,
        Type.getDescriptor(ModelType.class));
  }

  private void invokeSuperConstructor(MethodVisitor constructorVisitor, Type superclassType) {
    putThisOnStack(constructorVisitor);
    constructorVisitor.visitMethodInsn(
        INVOKESPECIAL,
        superclassType.getInternalName(),
        CONSTRUCTOR_NAME,
        Type.getMethodDescriptor(Type.VOID_TYPE),
        false);
  }

  private void writeToString(
      ClassVisitor visitor,
      Type generatedType,
      Class<?> viewClass,
      StructSchema<?> delegateSchema) {
    Method toStringMethod = getToStringMethod(viewClass);

    if (toStringMethod != null && !toStringMethod.getDeclaringClass().equals(Object.class)) {
      writeNonAbstractMethodWrapper(visitor, generatedType, viewClass, toStringMethod);
    } else if (delegateSchema != null && delegateSchema.hasProperty("displayName")) {
      writeDelegatingToString(
          visitor, generatedType, Type.getType(delegateSchema.getType().getConcreteClass()));
    } else {
      writeDefaultToString(visitor, generatedType);
    }
  }

  private void writeDelegatingToString(
      ClassVisitor visitor, Type generatedType, Type delegateType) {
    MethodVisitor methodVisitor =
        declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
    putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        delegateType.getInternalName(),
        "getDisplayName",
        TO_STRING_METHOD_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ARETURN);
  }

  private void writeDefaultToString(ClassVisitor visitor, Type generatedType) {
    MethodVisitor methodVisitor =
        declareMethod(visitor, "toString", TO_STRING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);
    putStateFieldValueOnStack(methodVisitor, generatedType);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
        "getDisplayName",
        TO_STRING_METHOD_DESCRIPTOR,
        true);
    finishVisitingMethod(methodVisitor, ARETURN);
  }

  private Method getToStringMethod(Class<?> managedTypeClass) {
    try {
      return managedTypeClass.getMethod("toString");
    } catch (NoSuchMethodException e) {
      return null;
    }
  }

  private void writeGroovyMethods(ClassVisitor visitor, Class<?> managedTypeClass) {
    // Object propertyMissing(String name)
    MethodVisitor methodVisitor =
        declareMethod(
            visitor, "propertyMissing", GET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingPropertyException(name, <managed-type>.class)
    methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putFirstMethodArgumentOnStack(methodVisitor);
    putClassOnStack(methodVisitor, managedTypeClass);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_PROPERTY_EXCEPTION_TYPE,
        "<init>",
        MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);

    // Object propertyMissing(String name, Object value)

    methodVisitor =
        declareMethod(
            visitor, "propertyMissing", SET_PROPERTY_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingPropertyException(name, <managed-type>.class)
    methodVisitor.visitTypeInsn(NEW, MISSING_PROPERTY_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putFirstMethodArgumentOnStack(methodVisitor);
    putClassOnStack(methodVisitor, managedTypeClass);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_PROPERTY_EXCEPTION_TYPE,
        "<init>",
        MISSING_PROPERTY_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);

    // Object methodMissing(String name, Object args)
    methodVisitor =
        declareMethod(
            visitor, "methodMissing", METHOD_MISSING_METHOD_DESCRIPTOR, CONCRETE_SIGNATURE);

    // throw new MissingMethodException(name, <managed-type>.class, args)
    methodVisitor.visitTypeInsn(NEW, MISSING_METHOD_EXCEPTION_TYPE);
    methodVisitor.visitInsn(DUP);
    putMethodArgumentOnStack(methodVisitor, 1);
    putClassOnStack(methodVisitor, managedTypeClass);
    putMethodArgumentOnStack(methodVisitor, 2);
    methodVisitor.visitTypeInsn(CHECKCAST, OBJECT_ARRAY_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        MISSING_METHOD_EXCEPTION_TYPE,
        "<init>",
        MISSING_METHOD_EXCEPTION_CONSTRUCTOR_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor, ATHROW);
  }

  private void putClassOnStack(MethodVisitor methodVisitor, Class<?> managedTypeClass) {
    putConstantOnStack(methodVisitor, managedTypeClass.getName());
    methodVisitor.visitMethodInsn(
        INVOKESTATIC, CLASS_INTERNAL_NAME, "forName", FOR_NAME_METHOD_DESCRIPTOR, false);
  }

  private void writeManagedInstanceMethods(ClassVisitor visitor, Type generatedType) {
    writeManagedInstanceGetBackingNodeMethod(visitor, generatedType);
    writeManagedInstanceGetManagedTypeMethod(visitor, generatedType);
  }

  private void writeManagedInstanceGetBackingNodeMethod(ClassVisitor visitor, Type generatedType) {
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            "getBackingNode",
            GET_BACKING_NODE_METHOD_DESCRIPTOR,
            CONCRETE_SIGNATURE,
            ACC_PUBLIC | ACC_SYNTHETIC);
    putStateFieldValueOnStack(methodVisitor, generatedType);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
        "getBackingNode",
        GET_BACKING_NODE_METHOD_DESCRIPTOR,
        true);
    finishVisitingMethod(methodVisitor, ARETURN);
  }

  private void writeManagedInstanceGetManagedTypeMethod(ClassVisitor visitor, Type generatedType) {
    MethodVisitor managedTypeVisitor =
        declareMethod(
            visitor,
            "getManagedType",
            GET_MANAGED_TYPE_METHOD_DESCRIPTOR,
            CONCRETE_SIGNATURE,
            ACC_PUBLIC | ACC_SYNTHETIC);
    putManagedTypeFieldValueOnStack(managedTypeVisitor, generatedType);
    finishVisitingMethod(managedTypeVisitor, ARETURN);
  }

  private void assignStateField(MethodVisitor constructorVisitor, Type generatedType) {
    putThisOnStack(constructorVisitor);
    putFirstMethodArgumentOnStack(constructorVisitor);
    constructorVisitor.visitFieldInsn(
        PUTFIELD,
        generatedType.getInternalName(),
        STATE_FIELD_NAME,
        MODEL_ELEMENT_STATE_TYPE.getDescriptor());
  }

  private void assignTypeConverterField(MethodVisitor constructorVisitor, Type generatedType) {
    putThisOnStack(constructorVisitor);
    putSecondMethodArgumentOnStack(constructorVisitor);
    constructorVisitor.visitFieldInsn(
        PUTFIELD,
        generatedType.getInternalName(),
        TYPE_CONVERTER_FIELD_NAME,
        TYPE_CONVERTER_TYPE.getDescriptor());
  }

  private void assignDelegateField(
      MethodVisitor constructorVisitor, Type generatedType, Type delegateType) {
    putThisOnStack(constructorVisitor);
    putThirdMethodArgumentOnStack(constructorVisitor);
    constructorVisitor.visitFieldInsn(
        PUTFIELD,
        generatedType.getInternalName(),
        DELEGATE_FIELD_NAME,
        delegateType.getDescriptor());
  }

  private void setCanCallSettersField(
      MethodVisitor methodVisitor, Type generatedType, boolean canCallSetters) {
    putThisOnStack(methodVisitor);
    methodVisitor.visitLdcInsn(canCallSetters);
    methodVisitor.visitFieldInsn(
        PUTFIELD,
        generatedType.getInternalName(),
        CAN_CALL_SETTERS_FIELD_NAME,
        Type.BOOLEAN_TYPE.getDescriptor());
  }

  private void writePropertyMethods(
      ClassVisitor visitor,
      Type generatedType,
      StructSchema<?> viewSchema,
      StructSchema<?> delegateSchema) {
    Collection<String> delegatePropertyNames;
    if (delegateSchema != null) {
      delegatePropertyNames = delegateSchema.getPropertyNames();
    } else {
      delegatePropertyNames = Collections.emptySet();
    }
    Class<?> viewClass = viewSchema.getType().getConcreteClass();
    for (ModelProperty<?> property : viewSchema.getProperties()) {
      String propertyName = property.getName();

      writeConfigureMethod(visitor, generatedType, property);
      writeSetMethod(visitor, generatedType, property);
      createTypeConvertingSetter(visitor, generatedType, property);

      // Delegated properties are handled in writeDelegateMethods()
      if (delegatePropertyNames.contains(propertyName)) {
        continue;
      }
      switch (property.getStateManagementType()) {
        case MANAGED:
          writeGetters(visitor, generatedType, property);
          writeSetter(visitor, generatedType, property);
          break;

        case UNMANAGED:
          for (WeaklyTypeReferencingMethod<?, ?> getter : property.getGetters()) {
            Method getterMethod = getter.getMethod();
            if (!Modifier.isFinal(getterMethod.getModifiers())
                && !propertyName.equals("metaClass")) {
              writeNonAbstractMethodWrapper(visitor, generatedType, viewClass, getterMethod);
            }
          }
          break;
      }
    }
  }

  private void writeSetMethod(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (property.isWritable() && property.getSchema() instanceof ScalarValueSchema) {

      // TODO - should we support this?
      // Adds a void $propName(Object value) method that sets the value
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
              null);
      putThisOnStack(methodVisitor);
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEVIRTUAL,
          generatedType.getInternalName(),
          property.getSetter().getName(),
          Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE),
          false);
      finishVisitingMethod(methodVisitor);
    }
  }

  private void writeConfigureMethod(
      ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (!property.isWritable() && property.getSchema() instanceof CompositeSchema) {
      // Adds a void $propName(Closure<?> cl) method that delegates to model state

      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
              null);
      putStateFieldValueOnStack(methodVisitor, generatedType);
      putConstantOnStack(methodVisitor, property.getName());
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEINTERFACE,
          MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
          "apply",
          STATE_APPLY_METHOD_DESCRIPTOR,
          true);
      finishVisitingMethod(methodVisitor);
      return;
    }
    if (!property.isWritable() && property.getSchema() instanceof UnmanagedImplStructSchema) {
      UnmanagedImplStructSchema<?> structSchema =
          (UnmanagedImplStructSchema<?>) property.getSchema();
      if (!structSchema.isAnnotated()) {
        return;
      }

      // Adds a void $propName(Closure<?> cl) method that executes the closure
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              property.getName(),
              Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
              null);
      putThisOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKEVIRTUAL,
          generatedType.getInternalName(),
          property.getGetters().get(0).getName(),
          Type.getMethodDescriptor(Type.getType(property.getType().getConcreteClass())),
          false);
      putFirstMethodArgumentOnStack(methodVisitor);
      methodVisitor.visitMethodInsn(
          INVOKESTATIC,
          Type.getInternalName(ClosureBackedAction.class),
          "execute",
          Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE, CLOSURE_TYPE),
          false);
      finishVisitingMethod(methodVisitor);
      return;
    }

    // Adds a void $propName(Closure<?> cl) method that throws MME, to avoid attempts to convert
    // closure to something else
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            property.getName(),
            Type.getMethodDescriptor(Type.VOID_TYPE, CLOSURE_TYPE),
            null);
    putThisOnStack(methodVisitor);
    putConstantOnStack(methodVisitor, property.getName());
    methodVisitor.visitInsn(Opcodes.ICONST_1);
    methodVisitor.visitTypeInsn(Opcodes.ANEWARRAY, OBJECT_TYPE.getInternalName());
    methodVisitor.visitInsn(Opcodes.DUP);
    methodVisitor.visitInsn(Opcodes.ICONST_0);
    putFirstMethodArgumentOnStack(methodVisitor);
    methodVisitor.visitInsn(Opcodes.AASTORE);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        "methodMissing",
        METHOD_MISSING_METHOD_DESCRIPTOR,
        false);
    finishVisitingMethod(methodVisitor);
  }

  private void writeSetter(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    WeaklyTypeReferencingMethod<?, Void> weakSetter = property.getSetter();
    // There is no setter for this property
    if (weakSetter == null) {
      return;
    }

    String propertyName = property.getName();
    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Label calledOutsideOfConstructor = new Label();

    Method setter = weakSetter.getMethod();

    // the regular typed setter
    String methodDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, propertyType);
    MethodVisitor methodVisitor =
        declareMethod(
            visitor, setter.getName(), methodDescriptor, AsmClassGeneratorUtils.signature(setter));

    putCanCallSettersFieldValueOnStack(methodVisitor, generatedType);
    jumpToLabelIfStackEvaluatesToTrue(methodVisitor, calledOutsideOfConstructor);
    throwExceptionBecauseCalledOnItself(methodVisitor);

    methodVisitor.visitLabel(calledOutsideOfConstructor);
    putStateFieldValueOnStack(methodVisitor, generatedType);
    putConstantOnStack(methodVisitor, propertyName);
    putFirstMethodArgumentOnStack(methodVisitor, propertyType);
    if (propertyClass.isPrimitive()) {
      boxType(methodVisitor, propertyClass);
    }
    invokeStateSetMethod(methodVisitor);

    finishVisitingMethod(methodVisitor);
  }

  // the overload of type Object for Groovy coercions:  public void setFoo(Object foo)
  private void createTypeConvertingSetter(
      ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    if (!property.isWritable() || !(property.getSchema() instanceof ScalarValueSchema)) {
      return;
    }

    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Class<?> boxedClass =
        propertyClass.isPrimitive() ? BOXED_TYPES.get(propertyClass) : propertyClass;
    Type boxedType = Type.getType(boxedClass);

    Method setter = property.getSetter().getMethod();
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            setter.getName(),
            SET_OBJECT_PROPERTY_DESCRIPTOR,
            SET_OBJECT_PROPERTY_DESCRIPTOR);

    putThisOnStack(methodVisitor);
    putTypeConverterFieldValueOnStack(methodVisitor, generatedType);

    // Object converted = $typeConverter.convert(foo, Float.class, false);
    methodVisitor.visitVarInsn(ALOAD, 1); // put var #1 ('foo') on the stack
    methodVisitor.visitLdcInsn(boxedType); // push the constant Class onto the stack
    methodVisitor.visitInsn(
        propertyClass.isPrimitive()
            ? ICONST_1
            : ICONST_0); // push int 1 or 0 (interpreted as true or false) onto the stack
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        TYPE_CONVERTER_TYPE.getInternalName(),
        "convert",
        COERCE_TO_SCALAR_DESCRIPTOR,
        true);
    methodVisitor.visitTypeInsn(CHECKCAST, boxedType.getInternalName());

    if (propertyClass.isPrimitive()) {
      unboxType(methodVisitor, propertyClass);
    }

    // invoke the typed setter, popping 'this' and 'converted' from the stack
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        setter.getName(),
        Type.getMethodDescriptor(Type.VOID_TYPE, propertyType),
        false);
    finishVisitingMethod(methodVisitor);
  }

  private void writeHashCodeMethod(ClassVisitor visitor, Type generatedType) {
    MethodVisitor methodVisitor =
        declareMethod(visitor, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, null);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        "getBackingNode",
        GET_BACKING_NODE_METHOD_DESCRIPTOR,
        false);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "hashCode", HASH_CODE_METHOD_DESCRIPTOR, true);
    methodVisitor.visitInsn(IRETURN);
    finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
  }

  private void writeEqualsMethod(ClassVisitor cw, Type generatedType) {
    MethodVisitor methodVisitor =
        cw.visitMethod(Opcodes.ACC_PUBLIC, "equals", EQUALS_METHOD_DESCRIPTOR, null, null);
    methodVisitor.visitCode();
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitVarInsn(ALOAD, 1);
    Label notSameLabel = new Label();
    methodVisitor.visitJumpInsn(IF_ACMPNE, notSameLabel);
    methodVisitor.visitInsn(ICONST_1);
    methodVisitor.visitInsn(IRETURN);
    methodVisitor.visitLabel(notSameLabel);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitTypeInsn(INSTANCEOF, MANAGED_INSTANCE_TYPE);
    Label notManagedInstanceLabel = new Label();
    methodVisitor.visitJumpInsn(IFNE, notManagedInstanceLabel);
    methodVisitor.visitInsn(ICONST_0);
    methodVisitor.visitInsn(IRETURN);
    methodVisitor.visitLabel(notManagedInstanceLabel);
    methodVisitor.visitVarInsn(ALOAD, 0);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        generatedType.getInternalName(),
        "getBackingNode",
        GET_BACKING_NODE_METHOD_DESCRIPTOR,
        false);
    methodVisitor.visitVarInsn(ALOAD, 1);
    methodVisitor.visitTypeInsn(CHECKCAST, MANAGED_INSTANCE_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        MANAGED_INSTANCE_TYPE,
        "getBackingNode",
        GET_BACKING_NODE_METHOD_DESCRIPTOR,
        true);
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE, MUTABLE_MODEL_NODE_TYPE, "equals", EQUALS_METHOD_DESCRIPTOR, true);
    finishVisitingMethod(methodVisitor, Opcodes.IRETURN);
  }

  private void throwExceptionBecauseCalledOnItself(MethodVisitor methodVisitor) {
    String exceptionInternalName = Type.getInternalName(UnsupportedOperationException.class);
    methodVisitor.visitTypeInsn(NEW, exceptionInternalName);
    methodVisitor.visitInsn(DUP);
    putConstantOnStack(methodVisitor, "Calling setters of a managed type on itself is not allowed");

    String constructorDescriptor = Type.getMethodDescriptor(Type.VOID_TYPE, STRING_TYPE);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL, exceptionInternalName, CONSTRUCTOR_NAME, constructorDescriptor, false);
    methodVisitor.visitInsn(ATHROW);
  }

  private void jumpToLabelIfStackEvaluatesToTrue(MethodVisitor methodVisitor, Label label) {
    methodVisitor.visitJumpInsn(IFNE, label);
  }

  private void invokeStateSetMethod(MethodVisitor methodVisitor) {
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
        "set",
        STATE_SET_METHOD_DESCRIPTOR,
        true);
  }

  private void putConstantOnStack(MethodVisitor methodVisitor, Object value) {
    methodVisitor.visitLdcInsn(value);
  }

  private MethodVisitor declareMethod(ClassVisitor visitor, Method method) {
    return declareMethod(visitor, method.getName(), Type.getMethodDescriptor(method));
  }

  private MethodVisitor declareMethod(
      ClassVisitor visitor, String methodName, String methodDescriptor) {
    return declareMethod(visitor, methodName, methodDescriptor, CONCRETE_SIGNATURE);
  }

  private MethodVisitor declareMethod(
      ClassVisitor visitor, String methodName, String methodDescriptor, String methodSignature) {
    return declareMethod(visitor, methodName, methodDescriptor, methodSignature, ACC_PUBLIC);
  }

  private MethodVisitor declareMethod(
      ClassVisitor visitor,
      String methodName,
      String methodDescriptor,
      String methodSignature,
      int access) {
    MethodVisitor methodVisitor =
        visitor.visitMethod(access, methodName, methodDescriptor, methodSignature, NO_EXCEPTIONS);
    methodVisitor.visitCode();
    return methodVisitor;
  }

  private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor, Type argType) {
    int loadCode = argType.getOpcode(ILOAD);
    methodVisitor.visitVarInsn(loadCode, 1);
  }

  private void putFirstMethodArgumentOnStack(MethodVisitor methodVisitor) {
    putFirstMethodArgumentOnStack(methodVisitor, OBJECT_TYPE);
  }

  private void putSecondMethodArgumentOnStack(MethodVisitor methodVisitor) {
    methodVisitor.visitVarInsn(ALOAD, 2);
  }

  private void putThirdMethodArgumentOnStack(MethodVisitor methodVisitor) {
    methodVisitor.visitVarInsn(ALOAD, 3);
  }

  private void putMethodArgumentOnStack(MethodVisitor methodVisitor, int index) {
    methodVisitor.visitVarInsn(ALOAD, index);
  }

  private void putMethodArgumentOnStack(MethodVisitor methodVisitor, Type type, int index) {
    methodVisitor.visitVarInsn(type.getOpcode(ILOAD), index);
  }

  private void putStateFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
    putFieldValueOnStack(methodVisitor, generatedType, STATE_FIELD_NAME, MODEL_ELEMENT_STATE_TYPE);
  }

  private void putTypeConverterFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
    putFieldValueOnStack(
        methodVisitor, generatedType, TYPE_CONVERTER_FIELD_NAME, TYPE_CONVERTER_TYPE);
  }

  private void putManagedTypeFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
    putStaticFieldValueOnStack(
        methodVisitor, generatedType, MANAGED_TYPE_FIELD_NAME, MODELTYPE_TYPE);
  }

  private void putDelegateFieldValueOnStack(
      MethodVisitor methodVisitor, Type generatedType, Type delegateType) {
    putFieldValueOnStack(methodVisitor, generatedType, DELEGATE_FIELD_NAME, delegateType);
  }

  private void putCanCallSettersFieldValueOnStack(MethodVisitor methodVisitor, Type generatedType) {
    putFieldValueOnStack(
        methodVisitor, generatedType, CAN_CALL_SETTERS_FIELD_NAME, Type.BOOLEAN_TYPE);
  }

  private void putFieldValueOnStack(
      MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) {
    putThisOnStack(methodVisitor);
    methodVisitor.visitFieldInsn(
        GETFIELD, generatedType.getInternalName(), name, fieldType.getDescriptor());
  }

  private void putStaticFieldValueOnStack(
      MethodVisitor methodVisitor, Type generatedType, String name, Type fieldType) {
    methodVisitor.visitFieldInsn(
        GETSTATIC, generatedType.getInternalName(), name, fieldType.getDescriptor());
  }

  private void writeGetters(ClassVisitor visitor, Type generatedType, ModelProperty<?> property) {
    Class<?> propertyClass = property.getType().getConcreteClass();
    Type propertyType = Type.getType(propertyClass);
    Set<String> processedNames = Sets.newHashSet();
    for (WeaklyTypeReferencingMethod<?, ?> weakGetter : property.getGetters()) {
      Method getter = weakGetter.getMethod();
      if (!processedNames.add(getter.getName())) {
        continue;
      }
      MethodVisitor methodVisitor =
          declareMethod(
              visitor,
              getter.getName(),
              Type.getMethodDescriptor(propertyType),
              AsmClassGeneratorUtils.signature(getter));

      putStateFieldValueOnStack(methodVisitor, generatedType);
      putConstantOnStack(methodVisitor, property.getName());
      invokeStateGetMethod(methodVisitor);
      castFirstStackElement(methodVisitor, propertyClass);
      finishVisitingMethod(methodVisitor, returnCode(propertyType));
    }
  }

  private int returnCode(Type returnType) {
    return returnType.getOpcode(IRETURN);
  }

  private void castFirstStackElement(MethodVisitor methodVisitor, Class<?> returnType) {
    if (returnType.isPrimitive()) {
      unboxType(methodVisitor, returnType);
    } else {
      methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(returnType));
    }
  }

  private void boxType(MethodVisitor methodVisitor, Class<?> primitiveType) {
    Class<?> boxedType = BOXED_TYPES.get(primitiveType);
    methodVisitor.visitMethodInsn(
        INVOKESTATIC,
        Type.getInternalName(boxedType),
        "valueOf",
        "(" + Type.getDescriptor(primitiveType) + ")" + Type.getDescriptor(boxedType),
        false);
  }

  private void unboxType(MethodVisitor methodVisitor, Class<?> primitiveClass) {
    // Float f = (Float) tmp
    // f==null?0:f.floatValue()
    Class<?> boxedType = BOXED_TYPES.get(primitiveClass);
    Type primitiveType = Type.getType(primitiveClass);
    methodVisitor.visitTypeInsn(CHECKCAST, Type.getInternalName(boxedType));
    methodVisitor.visitInsn(DUP);
    Label exit = new Label();
    Label elseValue = new Label();
    methodVisitor.visitJumpInsn(IFNONNULL, elseValue);
    methodVisitor.visitInsn(POP);
    pushDefaultValue(methodVisitor, primitiveClass);
    methodVisitor.visitJumpInsn(GOTO, exit);
    methodVisitor.visitLabel(elseValue);
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        Type.getInternalName(boxedType),
        primitiveClass.getSimpleName() + "Value",
        Type.getMethodDescriptor(primitiveType),
        false);
    methodVisitor.visitLabel(exit);
  }

  private void pushDefaultValue(MethodVisitor methodVisitor, Class<?> primitiveType) {
    int ins = ICONST_0;
    if (long.class == primitiveType) {
      ins = LCONST_0;
    } else if (double.class == primitiveType) {
      ins = DCONST_0;
    } else if (float.class == primitiveType) {
      ins = FCONST_0;
    }
    methodVisitor.visitInsn(ins);
  }

  private void invokeStateGetMethod(MethodVisitor methodVisitor) {
    methodVisitor.visitMethodInsn(
        INVOKEINTERFACE,
        MODEL_ELEMENT_STATE_TYPE_INTERNAL_NAME,
        "get",
        STATE_GET_METHOD_DESCRIPTOR,
        true);
  }

  private void writeNonAbstractMethodWrapper(
      ClassVisitor visitor, Type generatedType, Class<?> managedTypeClass, Method method) {
    Label start = new Label();
    Label end = new Label();
    Label handler = new Label();

    MethodVisitor methodVisitor = declareMethod(visitor, method);

    methodVisitor.visitTryCatchBlock(start, end, handler, null);

    setCanCallSettersField(methodVisitor, generatedType, false);

    methodVisitor.visitLabel(start);
    invokeSuperMethod(methodVisitor, managedTypeClass, method);
    methodVisitor.visitLabel(end);

    setCanCallSettersField(methodVisitor, generatedType, true);
    methodVisitor.visitInsn(ARETURN);

    methodVisitor.visitLabel(handler);
    setCanCallSettersField(methodVisitor, generatedType, true);
    methodVisitor.visitInsn(ATHROW);

    methodVisitor.visitMaxs(0, 0);
    methodVisitor.visitEnd();
  }

  private void writeDelegateMethods(
      final ClassVisitor visitor,
      final Type generatedType,
      StructSchema<?> delegateSchema,
      Set<Class<?>> typesToDelegate) {
    Class<?> delegateClass = delegateSchema.getType().getConcreteClass();
    Type delegateType = Type.getType(delegateClass);
    Map<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> methodsToDelegate = Maps.newHashMap();
    for (Class<?> typeToDelegate : typesToDelegate) {
      for (Method methodToDelegate : typeToDelegate.getMethods()) {
        if (ModelSchemaUtils.isIgnoredMethod(methodToDelegate)) {
          continue;
        }
        Equivalence.Wrapper<Method> methodKey = METHOD_EQUIVALENCE.wrap(methodToDelegate);
        Map<Class<?>, Method> methodsByReturnType = methodsToDelegate.get(methodKey);
        if (methodsByReturnType == null) {
          methodsByReturnType = Maps.newHashMap();
          methodsToDelegate.put(methodKey, methodsByReturnType);
        }
        methodsByReturnType.put(methodToDelegate.getReturnType(), methodToDelegate);
      }
    }
    Set<Equivalence.Wrapper<Method>> delegateMethodKeys =
        ImmutableSet.copyOf(
            Iterables.transform(
                Arrays.asList(delegateClass.getMethods()),
                new Function<Method, Equivalence.Wrapper<Method>>() {
                  @Override
                  public Equivalence.Wrapper<Method> apply(Method method) {
                    return METHOD_EQUIVALENCE.wrap(method);
                  }
                }));
    for (Map.Entry<Equivalence.Wrapper<Method>, Map<Class<?>, Method>> entry :
        methodsToDelegate.entrySet()) {
      Equivalence.Wrapper<Method> methodKey = entry.getKey();
      if (!delegateMethodKeys.contains(methodKey)) {
        continue;
      }

      Map<Class<?>, Method> methodsByReturnType = entry.getValue();
      for (Method methodToDelegate : methodsByReturnType.values()) {
        writeDelegatedMethod(visitor, generatedType, delegateType, methodToDelegate);
      }
    }
  }

  private void writeDelegatedMethod(
      ClassVisitor visitor, Type generatedType, Type delegateType, Method method) {
    MethodVisitor methodVisitor =
        declareMethod(
            visitor,
            method.getName(),
            Type.getMethodDescriptor(method),
            AsmClassGeneratorUtils.signature(method));
    invokeDelegateMethod(methodVisitor, generatedType, delegateType, method);
    Class<?> returnType = method.getReturnType();
    finishVisitingMethod(methodVisitor, returnCode(Type.getType(returnType)));
  }

  private void invokeDelegateMethod(
      MethodVisitor methodVisitor, Type generatedType, Type delegateType, Method method) {
    putDelegateFieldValueOnStack(methodVisitor, generatedType, delegateType);
    Class<?>[] parameterTypes = method.getParameterTypes();
    for (int paramNo = 0; paramNo < parameterTypes.length; paramNo++) {
      putMethodArgumentOnStack(methodVisitor, Type.getType(parameterTypes[paramNo]), paramNo + 1);
    }
    methodVisitor.visitMethodInsn(
        INVOKEVIRTUAL,
        delegateType.getInternalName(),
        method.getName(),
        Type.getMethodDescriptor(method),
        false);
  }

  private void invokeSuperMethod(MethodVisitor methodVisitor, Class<?> superClass, Method method) {
    putThisOnStack(methodVisitor);
    methodVisitor.visitMethodInsn(
        INVOKESPECIAL,
        Type.getInternalName(superClass),
        method.getName(),
        Type.getMethodDescriptor(method),
        false);
  }
}
Example #17
0
/** @author Matthias Broecheler ([email protected]) */
public class StandardTitanTx extends TitanBlueprintsTransaction {

  private static final Logger log = LoggerFactory.getLogger(StandardTitanTx.class);

  private static final Map<Long, InternalRelation> EMPTY_DELETED_RELATIONS = ImmutableMap.of();
  private static final ConcurrentMap<UniqueLockApplication, Lock> UNINITIALIZED_LOCKS = null;

  private static final long DEFAULT_CACHE_SIZE = 10000;

  private final StandardTitanGraph graph;
  private final TransactionConfig config;
  private final IDInspector idInspector;
  private final BackendTransaction txHandle;

  // Internal data structures
  private final VertexCache vertexCache;
  private final AtomicLong temporaryID;
  private final AddedRelationsContainer addedRelations;
  private Map<Long, InternalRelation> deletedRelations;
  private final Cache<StandardElementQuery, List<Object>> indexCache;
  private final IndexCache newVertexIndexEntries;
  private ConcurrentMap<UniqueLockApplication, Lock> uniqueLocks;

  private final Map<String, TitanType> typeCache;

  private boolean isOpen;

  public StandardTitanTx(
      StandardTitanGraph graph, TransactionConfig config, BackendTransaction txHandle) {
    Preconditions.checkNotNull(graph);
    Preconditions.checkArgument(graph.isOpen());
    Preconditions.checkNotNull(config);
    Preconditions.checkNotNull(txHandle);
    this.graph = graph;
    this.config = config;
    this.idInspector = graph.getIDInspector();
    this.txHandle = txHandle;

    temporaryID = new AtomicLong(-1);
    Cache<StandardElementQuery, List<Object>> indexCacheBuilder =
        CacheBuilder.newBuilder()
            .weigher(
                new Weigher<StandardElementQuery, List<Object>>() {
                  @Override
                  public int weigh(StandardElementQuery q, List<Object> r) {
                    return 2 + r.size();
                  }
                })
            .maximumWeight(DEFAULT_CACHE_SIZE)
            .build();
    int concurrencyLevel;
    if (config.isSingleThreaded()) {
      vertexCache = new SimpleVertexCache();
      addedRelations = new SimpleBufferAddedRelations();
      concurrencyLevel = 1;
      typeCache = new HashMap<String, TitanType>();
      newVertexIndexEntries = new SimpleIndexCache();
    } else {
      vertexCache = new ConcurrentVertexCache();
      addedRelations = new ConcurrentBufferAddedRelations();
      concurrencyLevel = 4;
      typeCache = new ConcurrentHashMap<String, TitanType>();
      newVertexIndexEntries = new ConcurrentIndexCache();
    }
    for (SystemType st : SystemKey.values()) typeCache.put(st.getName(), st);

    indexCache =
        CacheBuilder.newBuilder()
            .weigher(
                new Weigher<StandardElementQuery, List<Object>>() {
                  @Override
                  public int weigh(StandardElementQuery q, List<Object> r) {
                    return 2 + r.size();
                  }
                })
            .concurrencyLevel(concurrencyLevel)
            .maximumWeight(DEFAULT_CACHE_SIZE)
            .build();

    uniqueLocks = UNINITIALIZED_LOCKS;
    deletedRelations = EMPTY_DELETED_RELATIONS;
    this.isOpen = true;
  }

  /*
   * ------------------------------------ Utility Access Verification methods ------------------------------------
   */

  private final void verifyWriteAccess(TitanVertex... vertices) {
    if (config.isReadOnly())
      throw new UnsupportedOperationException(
          "Cannot create new entities in read-only transaction");
    verifyAccess(vertices);
  }

  public final void verifyAccess(TitanVertex... vertices) {
    verifyOpen();
    for (TitanVertex v : vertices) {
      Preconditions.checkArgument(v instanceof InternalVertex, "Invalid vertex: %s", v);
      if (!(v instanceof SystemType) && this != ((InternalVertex) v).tx())
        throw new IllegalArgumentException(
            "The vertex or type is not associated with this transaction [" + v + "]");
      if (v.isRemoved())
        throw new IllegalArgumentException("The vertex or type has been removed [" + v + "]");
    }
  }

  private final void verifyOpen() {
    if (isClosed())
      throw new IllegalStateException(
          "Operation cannot be executed because the enclosing transaction is closed");
  }

  /*
   * ------------------------------------ External Access ------------------------------------
   */

  public StandardTitanTx getNextTx() {
    Preconditions.checkArgument(isClosed());
    if (!config.isThreadBound())
      throw new IllegalStateException(
          "Cannot access element because its enclosing transaction is closed and unbound");
    else return (StandardTitanTx) graph.getCurrentThreadTx();
  }

  public TransactionConfig getConfiguration() {
    return config;
  }

  public StandardTitanGraph getGraph() {
    return graph;
  }

  public BackendTransaction getTxHandle() {
    return txHandle;
  }

  /*
   * ------------------------------------ Vertex Handling ------------------------------------
   */

  @Override
  public boolean containsVertex(final long vertexid) {
    verifyOpen();
    if (vertexCache.contains(vertexid)) {
      if (!vertexCache.get(vertexid, vertexConstructor).isRemoved()) return true;
      else return false;
    } else if (vertexid > 0 && graph.containsVertexID(vertexid, txHandle)) return true;
    else return false;
  }

  @Override
  public TitanVertex getVertex(final long id) {
    verifyOpen();
    if (config.hasVerifyVertexExistence() && !containsVertex(id)) return null;
    return getExistingVertex(id);
  }

  public InternalVertex getExistingVertex(final long id) {
    // return vertex no matter what, even if deleted
    InternalVertex vertex = vertexCache.get(id, vertexConstructor);
    return vertex;
  }

  private final Retriever<Long, InternalVertex> vertexConstructor =
      new Retriever<Long, InternalVertex>() {
        @Override
        public InternalVertex get(Long id) {
          Preconditions.checkNotNull(id);
          Preconditions.checkArgument(id > 0);

          InternalVertex vertex = null;
          if (idInspector.isTypeID(id)) {
            Preconditions.checkArgument(id > 0);
            if (idInspector.isPropertyKeyID(id)) {
              vertex = new TitanKeyVertex(StandardTitanTx.this, id, ElementLifeCycle.Loaded);
            } else {
              Preconditions.checkArgument(idInspector.isEdgeLabelID(id));
              vertex = new TitanLabelVertex(StandardTitanTx.this, id, ElementLifeCycle.Loaded);
            }
            // If its a newly created type, add to type cache
            typeCache.put(((TitanType) vertex).getName(), (TitanType) vertex);
          } else if (idInspector.isVertexID(id)) {
            vertex = new CacheVertex(StandardTitanTx.this, id, ElementLifeCycle.Loaded);
          } else throw new IllegalArgumentException("ID could not be recognized");
          return vertex;
        }
      };

  @Override
  public TitanVertex addVertex() {
    verifyWriteAccess();
    StandardVertex vertex =
        new StandardVertex(this, temporaryID.decrementAndGet(), ElementLifeCycle.New);
    vertex.addProperty(SystemKey.VertexState, (byte) 0);
    if (config.hasAssignIDsImmediately()) graph.assignID(vertex);
    vertexCache.add(vertex, vertex.getID());
    return vertex;
  }

  @Override
  public Iterable<Vertex> getVertices() {
    if (!addedRelations.isEmpty()) {
      // There are possible new vertices
      List<Vertex> newVs = new ArrayList<Vertex>();
      for (InternalVertex v : vertexCache.getAll()) {
        if (v.isNew() && !(v instanceof TitanType)) newVs.add(v);
      }
      return Iterables.concat(newVs, new VertexIterable(graph, this));
    } else {
      return (Iterable) new VertexIterable(graph, this);
    }
  }

  /*
   * ------------------------------------ Adding and Removing Relations ------------------------------------
   */

  private static final boolean isVertexIndexProperty(InternalRelation relation) {
    if (!(relation instanceof TitanProperty)) return false;
    return isVertexIndexProperty(((TitanProperty) relation).getPropertyKey());
  }

  private static final boolean isVertexIndexProperty(TitanKey key) {
    return key.hasIndex(Titan.Token.STANDARD_INDEX, Vertex.class);
  }

  public void removeRelation(InternalRelation relation) {
    Preconditions.checkArgument(!relation.isRemoved());
    relation = relation.it();
    // Delete from Vertex
    for (int i = 0; i < relation.getLen(); i++) {
      relation.getVertex(i).removeRelation(relation);
    }
    // Update transaction data structures
    if (relation.isNew()) {
      addedRelations.remove(relation);
      if (isVertexIndexProperty(relation)) newVertexIndexEntries.remove((TitanProperty) relation);
    } else {
      Preconditions.checkArgument(relation.isLoaded());
      if (deletedRelations == EMPTY_DELETED_RELATIONS) {
        if (config.isSingleThreaded()) {
          deletedRelations = new HashMap<Long, InternalRelation>();
        } else {
          synchronized (this) {
            if (deletedRelations == EMPTY_DELETED_RELATIONS)
              deletedRelations = new ConcurrentHashMap<Long, InternalRelation>();
          }
        }
      }
      deletedRelations.put(Long.valueOf(relation.getID()), relation);
    }
  }

  public boolean isRemovedRelation(Long relationId) {
    return deletedRelations.containsKey(relationId);
  }

  private Lock getUniquenessLock(final TitanVertex start, final TitanType type, final Object end) {
    if (config.isSingleThreaded()) return FakeLock.INSTANCE;
    if (uniqueLocks == UNINITIALIZED_LOCKS) {
      Preconditions.checkArgument(!config.isSingleThreaded());
      synchronized (this) {
        if (uniqueLocks == UNINITIALIZED_LOCKS)
          uniqueLocks = new ConcurrentHashMap<UniqueLockApplication, Lock>();
      }
    }
    UniqueLockApplication la = new UniqueLockApplication(start, type, end);
    Lock lock = new ReentrantLock();
    Lock existingLock = uniqueLocks.putIfAbsent(la, lock);
    if (existingLock == null) return lock;
    else return existingLock;
  }

  @Override
  public TitanEdge addEdge(TitanVertex outVertex, TitanVertex inVertex, TitanLabel label) {
    verifyWriteAccess(outVertex, inVertex);
    outVertex = ((InternalVertex) outVertex).it();
    inVertex = ((InternalVertex) inVertex).it();
    Preconditions.checkNotNull(label);
    Lock uniqueLock = FakeLock.INSTANCE;
    if (config.hasVerifyUniqueness()
        && (label.isUnique(Direction.OUT) || label.isUnique(Direction.IN)))
      uniqueLock = getUniquenessLock(outVertex, label, inVertex);
    uniqueLock.lock();
    try {
      // Check uniqueness
      if (config.hasVerifyUniqueness()) {
        if (label.isUnique(Direction.OUT)) {
          Preconditions.checkArgument(
              Iterables.isEmpty(
                  query(outVertex)
                      .includeHidden()
                      .type(label)
                      .direction(Direction.OUT)
                      .titanEdges()),
              "An edge with the given type already exists on the out-vertex");
        }
        if (label.isUnique(Direction.IN)) {
          Preconditions.checkArgument(
              Iterables.isEmpty(
                  query(inVertex).includeHidden().type(label).direction(Direction.IN).titanEdges()),
              "An edge with the given type already exists on the in-vertex");
        }
      }
      StandardEdge edge =
          new StandardEdge(
              temporaryID.decrementAndGet(),
              label,
              (InternalVertex) outVertex,
              (InternalVertex) inVertex,
              ElementLifeCycle.New);
      if (config.hasAssignIDsImmediately()) graph.assignID(edge);
      connectRelation(edge);
      return edge;
    } finally {
      uniqueLock.unlock();
    }
  }

  private void connectRelation(InternalRelation r) {
    for (int i = 0; i < r.getLen(); i++) {
      boolean success = r.getVertex(i).addRelation(r);
      if (!success) throw new AssertionError("Could not connect relation: " + r);
    }
    addedRelations.add(r);
    if (isVertexIndexProperty(r)) newVertexIndexEntries.add((TitanProperty) r);
  }

  @Override
  public TitanProperty addProperty(TitanVertex vertex, TitanKey key, Object value) {
    if (key.isUnique(Direction.OUT)) return setProperty(vertex, key, value);
    else return addPropertyInternal(vertex, key, value);
  }

  public TitanProperty addPropertyInternal(TitanVertex vertex, TitanKey key, Object value) {
    verifyWriteAccess(vertex);
    vertex = ((InternalVertex) vertex).it();
    Preconditions.checkNotNull(key);
    value = AttributeUtil.verifyAttribute(key, value);
    Lock uniqueLock = FakeLock.INSTANCE;
    if (config.hasVerifyUniqueness() && (key.isUnique(Direction.OUT) || key.isUnique(Direction.IN)))
      uniqueLock = getUniquenessLock(vertex, key, value);
    uniqueLock.lock();
    try {
      // Check uniqueness
      if (config.hasVerifyUniqueness()) {
        if (key.isUnique(Direction.OUT)) {
          Preconditions.checkArgument(
              Iterables.isEmpty(
                  query(vertex).includeHidden().type(key).direction(Direction.OUT).properties()),
              "An property with the given key already exists on the vertex and the property key is defined as out-unique");
        }
        if (key.isUnique(Direction.IN)) {
          Preconditions.checkArgument(
              Iterables.isEmpty(getVertices(key, value)),
              "The given value is already used as a property and the property key is defined as in-unique");
        }
      }
      StandardProperty prop =
          new StandardProperty(
              temporaryID.decrementAndGet(),
              key,
              (InternalVertex) vertex,
              value,
              ElementLifeCycle.New);
      if (config.hasAssignIDsImmediately()) graph.assignID(prop);
      connectRelation(prop);
      return prop;
    } finally {
      uniqueLock.unlock();
    }
  }

  public TitanProperty setProperty(TitanVertex vertex, final TitanKey key, Object value) {
    Preconditions.checkNotNull(key);
    Preconditions.checkArgument(
        key.isUnique(Direction.OUT), "Not an out-unique key: %s", key.getName());

    Lock uniqueLock = FakeLock.INSTANCE;
    try {
      if (config.hasVerifyUniqueness()) {
        // Acquire uniqueness lock, remove and add
        uniqueLock = getUniquenessLock(vertex, key, value);
        uniqueLock.lock();
        vertex.removeProperty(key);
      } else {
        // Only delete in-memory
        InternalVertex v = (InternalVertex) vertex;
        for (InternalRelation r :
            v.it()
                .getAddedRelations(
                    new Predicate<InternalRelation>() {
                      @Override
                      public boolean apply(@Nullable InternalRelation p) {
                        return p.getType().equals(key);
                      }
                    })) {
          r.remove();
        }
      }
      return addPropertyInternal(vertex, key, value);
    } finally {
      uniqueLock.unlock();
    }
  }

  @Override
  public Iterable<Edge> getEdges() {
    return new VertexCentricEdgeIterable(getVertices());
  }

  /*
   * ------------------------------------ Type Handling ------------------------------------
   */

  public TitanKey makePropertyKey(PropertyKeyDefinition definition) {
    verifyOpen();
    TitanKeyVertex prop =
        new TitanKeyVertex(this, temporaryID.decrementAndGet(), ElementLifeCycle.New);
    addProperty(prop, SystemKey.TypeName, definition.getName());
    addProperty(prop, SystemKey.PropertyKeyDefinition, definition);
    addProperty(prop, SystemKey.TypeClass, TitanTypeClass.KEY);
    graph.assignID(prop);
    Preconditions.checkArgument(prop.getID() > 0);
    vertexCache.add(prop, prop.getID());
    typeCache.put(definition.getName(), prop);
    return prop;
  }

  public TitanLabel makeEdgeLabel(EdgeLabelDefinition definition) {
    verifyOpen();
    TitanLabelVertex label =
        new TitanLabelVertex(this, temporaryID.decrementAndGet(), ElementLifeCycle.New);
    addProperty(label, SystemKey.TypeName, definition.getName());
    addProperty(label, SystemKey.RelationTypeDefinition, definition);
    addProperty(label, SystemKey.TypeClass, TitanTypeClass.LABEL);
    graph.assignID(label);
    vertexCache.add(label, label.getID());
    typeCache.put(definition.getName(), label);
    return label;
  }

  @Override
  public boolean containsType(String name) {
    verifyOpen();
    return (typeCache.containsKey(name)
        || !Iterables.isEmpty(getVertices(SystemKey.TypeName, name)));
  }

  @Override
  public TitanType getType(String name) {
    verifyOpen();
    TitanType type = typeCache.get(name);
    if (type == null)
      type = (TitanType) Iterables.getOnlyElement(getVertices(SystemKey.TypeName, name), null);
    return type;
  }

  public TitanType getExistingType(long typeid) {
    if (idInspector.getGroupID(typeid) == SystemTypeManager.SYSTEM_TYPE_GROUP.getID()) {
      // its a systemtype
      return SystemTypeManager.getSystemEdgeType(typeid);
    } else {
      InternalVertex v = getExistingVertex(typeid);
      Preconditions.checkArgument(v instanceof TitanType, "Given id is not a type: " + typeid);
      return (TitanType) v;
    }
  }

  @Override
  public TitanKey getPropertyKey(String name) {
    TitanType et = getType(name);
    if (et == null) {
      return config.getAutoEdgeTypeMaker().makeKey(name, makeType());
    } else if (et.isPropertyKey()) {
      return (TitanKey) et;
    } else throw new IllegalArgumentException("The type of given name is not a key: " + name);
  }

  @Override
  public TitanLabel getEdgeLabel(String name) {
    TitanType et = getType(name);
    if (et == null) {
      return config.getAutoEdgeTypeMaker().makeLabel(name, makeType());
    } else if (et.isEdgeLabel()) {
      return (TitanLabel) et;
    } else throw new IllegalArgumentException("The type of given name is not a label: " + name);
  }

  @Override
  public TypeMaker makeType() {
    return new StandardTypeMaker(this);
  }

  /*
   * ------------------------------------ Query Answering ------------------------------------
   */

  public VertexCentricQueryBuilder query(TitanVertex vertex) {
    return new VertexCentricQueryBuilder((InternalVertex) vertex);
  }

  public final QueryExecutor<VertexCentricQuery, TitanRelation> edgeProcessor =
      new QueryExecutor<VertexCentricQuery, TitanRelation>() {

        @Override
        public boolean hasNew(final VertexCentricQuery query) {
          return query.getVertex().isNew()
              || ((InternalVertex) query.getVertex()).hasAddedRelations();
        }

        @Override
        public Iterator<TitanRelation> getNew(final VertexCentricQuery query) {
          return (Iterator)
              ((InternalVertex) query.getVertex())
                  .getAddedRelations(
                      new Predicate<InternalRelation>() {
                        // Need to filter out self-loops if query only asks for one direction

                        private TitanRelation previous = null;

                        @Override
                        public boolean apply(@Nullable InternalRelation relation) {
                          if (query.hasSingleDirection()
                              && (relation instanceof TitanEdge)
                              && ((TitanEdge) relation).isLoop()) {
                            if (relation.equals(previous)) return false;
                            else previous = relation;
                          }
                          return query.matches(relation);
                        }
                      })
                  .iterator();
        }

        @Override
        public Iterator<TitanRelation> execute(final VertexCentricQuery query) {
          if (query.getVertex().isNew()) return Iterators.emptyIterator();

          final EdgeSerializer edgeSerializer = graph.getEdgeSerializer();
          FittedSliceQuery sq = edgeSerializer.getQuery(query);
          final boolean fittedQuery = sq.isFitted();
          final InternalVertex v = query.getVertex();
          final boolean needsFiltering = !sq.isFitted() || !deletedRelations.isEmpty();
          if (needsFiltering && sq.hasLimit())
            sq = new FittedSliceQuery(sq, QueryUtil.updateLimit(sq.getLimit(), 1.1));

          Iterable<TitanRelation> result = null;
          double limitMultiplier = 1.0;
          int previousDiskSize = 0;
          boolean finished;
          do {
            finished = true;
            Iterable<Entry> iter = null;

            if (v instanceof CacheVertex) {
              CacheVertex cv = (CacheVertex) v;
              iter =
                  ((CacheVertex) v)
                      .loadRelations(
                          sq,
                          new Retriever<SliceQuery, List<Entry>>() {
                            @Override
                            public List<Entry> get(SliceQuery query) {
                              return graph.edgeQuery(v.getID(), query, txHandle);
                            }
                          });
            } else {
              iter = graph.edgeQuery(v.getID(), sq, txHandle);
            }
            result =
                Iterables.transform(
                    iter,
                    new Function<Entry, TitanRelation>() {
                      @Nullable
                      @Override
                      public TitanRelation apply(@Nullable Entry entry) {
                        return edgeSerializer.readRelation(v, entry);
                      }
                    });
            if (needsFiltering) {
              result =
                  Iterables.filter(
                      result,
                      new Predicate<TitanRelation>() {
                        @Override
                        public boolean apply(@Nullable TitanRelation relation) {
                          // Filter out updated and deleted relations
                          return (relation == ((InternalRelation) relation).it()
                                  && !deletedRelations.containsKey(Long.valueOf(relation.getID())))
                              && (fittedQuery || query.matches(relation));
                        }
                      });
            }
            // Determine termination
            if (needsFiltering && query.hasLimit()) {
              if (!IterablesUtil.sizeLargerOrEqualThan(result, query.getLimit())) {
                int currentDiskSize = IterablesUtil.size(iter);
                if (currentDiskSize > previousDiskSize) {
                  finished = false;
                  previousDiskSize = currentDiskSize;
                  limitMultiplier *= 2;
                  sq =
                      new FittedSliceQuery(
                          sq, QueryUtil.updateLimit(sq.getLimit(), limitMultiplier));
                }
              }
            }
          } while (!finished);

          return result.iterator();
        }
      };

  public final QueryExecutor<StandardElementQuery, TitanElement> elementProcessor =
      new QueryExecutor<StandardElementQuery, TitanElement>() {

        @Override
        public boolean hasNew(StandardElementQuery query) {
          if (query.getType() == StandardElementQuery.Type.VERTEX) return hasModifications();
          else if (query.getType() == StandardElementQuery.Type.EDGE)
            return !addedRelations.isEmpty();
          else throw new AssertionError("Unexpected type: " + query.getType());
        }

        @Override
        public Iterator<TitanElement> getNew(final StandardElementQuery query) {
          Preconditions.checkArgument(
              query.getType() == StandardElementQuery.Type.VERTEX
                  || query.getType() == StandardElementQuery.Type.EDGE);
          if (query.getType() == StandardElementQuery.Type.VERTEX && hasModifications()) {
            // Collect all keys from the query - ASSUMPTION: query is an AND of KeyAtom
            final Set<TitanKey> keys = Sets.newHashSet();
            KeyAtom<TitanKey> standardIndexKey = null;
            for (KeyCondition<TitanKey> cond : query.getCondition().getChildren()) {
              KeyAtom<TitanKey> atom = (KeyAtom<TitanKey>) cond;
              if (atom.getRelation() == Cmp.EQUAL && isVertexIndexProperty(atom.getKey()))
                standardIndexKey = atom;
              keys.add(atom.getKey());
            }
            Iterator<TitanVertex> vertices;
            if (standardIndexKey == null) {
              Set<TitanVertex> vertexSet = Sets.newHashSet();
              for (TitanRelation r :
                  addedRelations.getView(
                      new Predicate<InternalRelation>() {
                        @Override
                        public boolean apply(@Nullable InternalRelation relation) {
                          return keys.contains(relation.getType());
                        }
                      })) {
                vertexSet.add(((TitanProperty) r).getVertex());
              }
              for (TitanRelation r : deletedRelations.values()) {
                if (keys.contains(r.getType())) {
                  TitanVertex v = ((TitanProperty) r).getVertex();
                  if (!v.isRemoved()) vertexSet.add(v);
                }
              }
              vertices = vertexSet.iterator();
            } else {
              vertices =
                  Iterators.transform(
                      newVertexIndexEntries
                          .get(standardIndexKey.getCondition(), standardIndexKey.getKey())
                          .iterator(),
                      new Function<TitanProperty, TitanVertex>() {
                        @Nullable
                        @Override
                        public TitanVertex apply(@Nullable TitanProperty o) {
                          return o.getVertex();
                        }
                      });
            }

            return (Iterator)
                Iterators.filter(
                    vertices,
                    new Predicate<TitanVertex>() {
                      @Override
                      public boolean apply(@Nullable TitanVertex vertex) {
                        return query.matches(vertex);
                      }
                    });
          } else if (query.getType() == StandardElementQuery.Type.EDGE
              && !addedRelations.isEmpty()) {
            return (Iterator)
                addedRelations
                    .getView(
                        new Predicate<InternalRelation>() {
                          @Override
                          public boolean apply(@Nullable InternalRelation relation) {
                            return (relation instanceof TitanEdge)
                                && !relation.isHidden()
                                && query.matches(relation);
                          }
                        })
                    .iterator();
          } else throw new IllegalArgumentException("Unexpected type: " + query.getType());
        }

        private boolean isDeleted(StandardElementQuery query, TitanElement result) {
          if (result.isRemoved()) return true;
          else if (query.getType() == StandardElementQuery.Type.VERTEX) {
            Preconditions.checkArgument(result instanceof InternalVertex);
            InternalVertex v = ((InternalVertex) result).it();
            if (v.hasAddedRelations() || v.hasRemovedRelations()) {
              return !query.matches(result);
            } else return false;
          } else if (query.getType() == StandardElementQuery.Type.EDGE) {
            // Loaded edges are immutable and new edges are previously filtered
            Preconditions.checkArgument(result.isLoaded() || result.isNew());
            return false;
          } else throw new IllegalArgumentException("Unexpected type: " + query.getType());
        }

        @Override
        public Iterator<TitanElement> execute(final StandardElementQuery query) {
          Iterator<TitanElement> iter = null;
          if (!query.hasIndex()) {
            log.warn(
                "Query requires iterating over all vertices [{}]. For better performance, use indexes",
                query.getCondition());
            if (query.getType() == StandardElementQuery.Type.VERTEX) {
              iter = (Iterator) getVertices().iterator();
            } else if (query.getType() == StandardElementQuery.Type.EDGE) {
              iter = (Iterator) getEdges().iterator();
            } else throw new IllegalArgumentException("Unexpected type: " + query.getType());
            iter =
                Iterators.filter(
                    iter,
                    new Predicate<TitanElement>() {
                      @Override
                      public boolean apply(@Nullable TitanElement element) {
                        return query.matches(element);
                      }
                    });
          } else {
            String index = query.getIndex();
            log.debug("Answering query [{}] with index {}", query, index);
            // Filter out everything not covered by the index
            KeyCondition<TitanKey> condition = query.getCondition();
            // ASSUMPTION: query is an AND of KeyAtom
            Preconditions.checkArgument(condition instanceof KeyAnd);
            Preconditions.checkArgument(condition.hasChildren());
            List<KeyCondition<TitanKey>> newConds = Lists.newArrayList();

            boolean needsFilter = false;
            for (KeyCondition<TitanKey> c : condition.getChildren()) {
              KeyAtom<TitanKey> atom = (KeyAtom<TitanKey>) c;
              if (getGraph()
                      .getIndexInformation(index)
                      .supports(atom.getKey().getDataType(), atom.getRelation())
                  && atom.getKey().hasIndex(index, query.getType().getElementType())
                  && atom.getCondition() != null) {
                newConds.add(atom);
              } else {
                log.debug(
                    "Filtered out atom [{}] from query [{}] because it is not indexed or not covered by the index");
                needsFilter = true;
              }
            }
            Preconditions.checkArgument(
                !newConds.isEmpty(), "Invalid index assignment [%s] to query [%s]", index, query);
            final StandardElementQuery indexQuery;
            if (needsFilter) {
              Preconditions.checkArgument(
                  !newConds.isEmpty(),
                  "Query has been assigned an index [%s] in error: %s",
                  query.getIndex(),
                  query);
              indexQuery =
                  new StandardElementQuery(
                      query.getType(),
                      KeyAnd.of(newConds.toArray(new KeyAtom[newConds.size()])),
                      query.getLimit(),
                      index);
            } else {
              indexQuery = query;
            }
            try {
              iter =
                  Iterators.transform(
                      indexCache
                          .get(
                              indexQuery,
                              new Callable<List<Object>>() {
                                @Override
                                public List<Object> call() throws Exception {
                                  return graph.elementQuery(indexQuery, txHandle);
                                }
                              })
                          .iterator(),
                      new Function<Object, TitanElement>() {
                        @Nullable
                        @Override
                        public TitanElement apply(@Nullable Object id) {
                          Preconditions.checkNotNull(id);
                          if (id instanceof Long) return (TitanVertex) getVertex((Long) id);
                          else if (id instanceof RelationIdentifier)
                            return (TitanElement) getEdge((RelationIdentifier) id);
                          else throw new IllegalArgumentException("Unexpected id type: " + id);
                        }
                      });
            } catch (Exception e) {
              throw new TitanException("Could not call index", e);
            }
            if (needsFilter) {
              iter =
                  Iterators.filter(
                      iter,
                      new Predicate<TitanElement>() {
                        @Override
                        public boolean apply(@Nullable TitanElement element) {
                          return element != null
                              && !element.isRemoved()
                              && !isDeleted(query, element)
                              && query.matches(element);
                        }
                      });
            } else {
              iter =
                  Iterators.filter(
                      iter,
                      new Predicate<TitanElement>() {
                        @Override
                        public boolean apply(@Nullable TitanElement element) {
                          return element != null
                              && !element.isRemoved()
                              && !isDeleted(query, element);
                        }
                      });
            }
          }
          return iter;
        }
      };

  @Override
  public TitanGraphQueryBuilder query() {
    return new TitanGraphQueryBuilder(this);
  }

  @Override
  public Iterable<TitanVertex> getVertices(TitanKey key, Object attribute) {
    Preconditions.checkNotNull(key);
    Preconditions.checkNotNull(attribute);
    return (Iterable) query().has(key, Cmp.EQUAL, attribute).vertices();
  }

  @Override
  public TitanVertex getVertex(TitanKey key, Object attribute) {
    Preconditions.checkArgument(
        key.isUnique(Direction.IN), "Key is not uniquely associated to value [%s]", key.getName());
    return Iterables.getOnlyElement(getVertices(key, attribute), null);
  }

  @Override
  public TitanVertex getVertex(String key, Object attribute) {
    if (!containsType(key)) return null;
    else return getVertex((TitanKey) getType(key), attribute);
  }

  @Override
  public Iterable<TitanEdge> getEdges(TitanKey key, Object attribute) {
    Preconditions.checkNotNull(key);
    Preconditions.checkNotNull(attribute);
    return (Iterable) query().has(key, Cmp.EQUAL, attribute).edges();
  }

  /*
   * ------------------------------------ Transaction State ------------------------------------
   */

  @Override
  public synchronized void commit() {
    Preconditions.checkArgument(isOpen(), "The transaction has already been closed");
    try {
      if (hasModifications()) {
        graph.save(addedRelations.getAll(), deletedRelations.values(), this);
      }
      txHandle.commit();
    } catch (Exception e) {
      try {
        txHandle.rollback();
      } catch (StorageException e1) {
        throw new TitanException("Could not rollback after a failed commit", e);
      }
      throw new TitanException(
          "Could not commit transaction due to exception during persistence", e);
    } finally {
      close();
    }
  }

  @Override
  public synchronized void rollback() {
    Preconditions.checkArgument(isOpen(), "The transaction has already been closed");
    try {
      txHandle.rollback();
    } catch (Exception e) {
      throw new TitanException("Could not rollback transaction due to exception", e);
    } finally {
      close();
    }
  }

  private void close() {
    // TODO: release non crucial data structures to preserve memory?
    isOpen = false;
  }

  @Override
  public boolean isOpen() {
    return isOpen;
  }

  @Override
  public boolean isClosed() {
    return !isOpen;
  }

  @Override
  public boolean hasModifications() {
    return !addedRelations.isEmpty() || !deletedRelations.isEmpty();
  }
}
Example #18
0
  private void init(
      PName name,
      PTableType type,
      long timeStamp,
      long sequenceNumber,
      String pkName,
      List<PColumn> columns,
      PTableStats stats) {
    this.name = name;
    this.type = type;
    this.timeStamp = timeStamp;
    this.sequenceNumber = sequenceNumber;
    this.columnsByName = ArrayListMultimap.create(columns.size(), 1);
    this.pkName = pkName;
    List<PColumn> pkColumns = Lists.newArrayListWithExpectedSize(columns.size() - 1);
    PColumn[] allColumns = new PColumn[columns.size()];
    RowKeySchemaBuilder builder = new RowKeySchemaBuilder();
    for (int i = 0; i < allColumns.length; i++) {
      PColumn column = columns.get(i);
      allColumns[column.getPosition()] = column;
      PName familyName = column.getFamilyName();
      if (familyName == null) {
        pkColumns.add(column);
        builder.addField(column);
      }
      columnsByName.put(column.getName().getString(), column);
    }
    this.pkColumns = ImmutableList.copyOf(pkColumns);
    this.rowKeySchema = builder.setMinNullable(pkColumns.size()).build();
    this.allColumns = ImmutableList.copyOf(allColumns);

    // Two pass so that column order in column families matches overall column order
    // and to ensure that column family order is constant
    int maxExpectedSize = allColumns.length - pkColumns.size();
    // Maintain iteration order so that column families are ordered as they are listed
    Map<PName, List<PColumn>> familyMap = Maps.newLinkedHashMap();
    for (PColumn column : allColumns) {
      PName familyName = column.getFamilyName();
      if (familyName != null) {
        List<PColumn> columnsInFamily = familyMap.get(familyName);
        if (columnsInFamily == null) {
          columnsInFamily = Lists.newArrayListWithExpectedSize(maxExpectedSize);
          familyMap.put(familyName, columnsInFamily);
        }
        columnsInFamily.add(column);
      }
    }

    Iterator<Map.Entry<PName, List<PColumn>>> iterator = familyMap.entrySet().iterator();
    PColumnFamily[] families = new PColumnFamily[familyMap.size()];
    ImmutableMap.Builder<String, PColumnFamily> familyByString = ImmutableMap.builder();
    ImmutableSortedMap.Builder<byte[], PColumnFamily> familyByBytes =
        ImmutableSortedMap.orderedBy(Bytes.BYTES_COMPARATOR);
    for (int i = 0; i < families.length; i++) {
      Map.Entry<PName, List<PColumn>> entry = iterator.next();
      PColumnFamily family = new PColumnFamilyImpl(entry.getKey(), entry.getValue());
      families[i] = family;
      familyByString.put(family.getName().getString(), family);
      familyByBytes.put(family.getName().getBytes(), family);
    }
    this.families = ImmutableList.copyOf(families);
    this.familyByBytes = familyByBytes.build();
    this.familyByString = familyByString.build();
    this.stats = stats;
  }
Example #19
0
  /**
   * Parse the given array of arguments.
   *
   * <p>Empty arguments are removed from the list of arguments.
   *
   * @param args an array with arguments
   * @param expectedValueFlags a set containing all value flags (pass null to disable value flag
   *     parsing)
   * @param allowHangingFlag true if hanging flags are allowed
   * @param namespace the locals, null to create empty one
   * @throws CommandException thrown on a parsing error
   */
  public CommandContext(
      String[] args,
      Set<Character> expectedValueFlags,
      boolean allowHangingFlag,
      Namespace namespace)
      throws CommandException {
    if (expectedValueFlags == null) {
      expectedValueFlags = Collections.emptySet();
    }

    originalArgs = args;
    command = args[0];
    this.namespace = namespace != null ? namespace : new Namespace();
    boolean isHanging = false;
    SuggestionContext suggestionContext = SuggestionContext.hangingValue();

    // Eliminate empty args and combine multiword args first
    List<Integer> argIndexList = new ArrayList<Integer>(args.length);
    List<String> argList = new ArrayList<String>(args.length);
    for (int i = 1; i < args.length; ++i) {
      isHanging = false;

      String arg = args[i];
      if (arg.isEmpty()) {
        isHanging = true;
        continue;
      }

      argIndexList.add(i);

      switch (arg.charAt(0)) {
        case '\'':
        case '"':
          final StringBuilder build = new StringBuilder();
          final char quotedChar = arg.charAt(0);

          int endIndex;
          for (endIndex = i; endIndex < args.length; ++endIndex) {
            final String arg2 = args[endIndex];
            if (arg2.charAt(arg2.length() - 1) == quotedChar && arg2.length() > 1) {
              if (endIndex != i) build.append(' ');
              build.append(arg2.substring(endIndex == i ? 1 : 0, arg2.length() - 1));
              break;
            } else if (endIndex == i) {
              build.append(arg2.substring(1));
            } else {
              build.append(' ').append(arg2);
            }
          }

          if (endIndex < args.length) {
            arg = build.toString();
            i = endIndex;
          }

          // In case there is an empty quoted string
          if (arg.isEmpty()) {
            continue;
          }
          // else raise exception about hanging quotes?
      }
      argList.add(arg);
    }

    // Then flags

    List<Integer> originalArgIndices = Lists.newArrayListWithCapacity(argIndexList.size());
    List<String> parsedArgs = Lists.newArrayListWithCapacity(argList.size());
    Map<Character, String> valueFlags = Maps.newHashMap();
    List<Character> booleanFlags = Lists.newArrayList();

    for (int nextArg = 0; nextArg < argList.size(); ) {
      // Fetch argument
      String arg = argList.get(nextArg++);
      suggestionContext = SuggestionContext.hangingValue();

      // Not a flag?
      if (arg.charAt(0) != '-' || arg.length() == 1 || !arg.matches("^-[a-zA-Z\\?]+$")) {
        if (!isHanging) {
          suggestionContext = SuggestionContext.lastValue();
        }

        originalArgIndices.add(argIndexList.get(nextArg - 1));
        parsedArgs.add(arg);
        continue;
      }

      // Handle flag parsing terminator --
      if (arg.equals("--")) {
        while (nextArg < argList.size()) {
          originalArgIndices.add(argIndexList.get(nextArg));
          parsedArgs.add(argList.get(nextArg++));
        }
        break;
      }

      // Go through the flag characters
      for (int i = 1; i < arg.length(); ++i) {
        char flagName = arg.charAt(i);

        if (expectedValueFlags.contains(flagName)) {
          if (valueFlags.containsKey(flagName)) {
            throw new CommandException("Value flag '" + flagName + "' already given");
          }

          if (nextArg >= argList.size()) {
            if (allowHangingFlag) {
              suggestionContext = SuggestionContext.flag(flagName);
              break;
            } else {
              throw new CommandException("No value specified for the '-" + flagName + "' flag.");
            }
          }

          // If it is a value flag, read another argument and add it
          valueFlags.put(flagName, argList.get(nextArg++));
          if (!isHanging) {
            suggestionContext = SuggestionContext.flag(flagName);
          }
        } else {
          booleanFlags.add(flagName);
        }
      }
    }

    ImmutableMap.Builder<Character, String> allFlagsBuilder =
        new ImmutableMap.Builder<Character, String>().putAll(valueFlags);
    for (Character flag : booleanFlags) {
      allFlagsBuilder.put(flag, "true");
    }

    this.parsedArgs = ImmutableList.copyOf(parsedArgs);
    this.originalArgIndices = ImmutableList.copyOf(originalArgIndices);
    this.booleanFlags = ImmutableSet.copyOf(booleanFlags);
    this.valueFlags = ImmutableMap.copyOf(valueFlags);
    this.allFlags = allFlagsBuilder.build();
    this.suggestionContext = suggestionContext;
  }
Example #20
0
public class DocIndexMetaData {

  private static final String ID = "_id";
  public static final ColumnIdent ID_IDENT = new ColumnIdent(ID);
  private final IndexMetaData metaData;

  private final MappingMetaData defaultMappingMetaData;
  private final Map<String, Object> defaultMappingMap;

  private final Map<ColumnIdent, IndexReferenceInfo.Builder> indicesBuilder = new HashMap<>();

  private final ImmutableSortedSet.Builder<ReferenceInfo> columnsBuilder =
      ImmutableSortedSet.orderedBy(
          new Comparator<ReferenceInfo>() {
            @Override
            public int compare(ReferenceInfo o1, ReferenceInfo o2) {
              return o1.ident().columnIdent().fqn().compareTo(o2.ident().columnIdent().fqn());
            }
          });

  // columns should be ordered
  private final ImmutableMap.Builder<ColumnIdent, ReferenceInfo> referencesBuilder =
      ImmutableSortedMap.naturalOrder();
  private final ImmutableList.Builder<ReferenceInfo> partitionedByColumnsBuilder =
      ImmutableList.builder();

  private final TableIdent ident;
  private final int numberOfShards;
  private final BytesRef numberOfReplicas;
  private final ImmutableMap<String, Object> tableParameters;
  private Map<String, Object> metaMap;
  private Map<String, Object> metaColumnsMap;
  private Map<String, Object> indicesMap;
  private List<List<String>> partitionedByList;
  private ImmutableList<ReferenceInfo> columns;
  private ImmutableMap<ColumnIdent, IndexReferenceInfo> indices;
  private ImmutableList<ReferenceInfo> partitionedByColumns;
  private ImmutableMap<ColumnIdent, ReferenceInfo> references;
  private ImmutableList<ColumnIdent> primaryKey;
  private ColumnIdent routingCol;
  private ImmutableList<ColumnIdent> partitionedBy;
  private final boolean isAlias;
  private final Set<String> aliases;
  private boolean hasAutoGeneratedPrimaryKey = false;

  private ColumnPolicy columnPolicy = ColumnPolicy.DYNAMIC;

  private static final ImmutableMap<String, DataType> dataTypeMap =
      ImmutableMap.<String, DataType>builder()
          .put("date", DataTypes.TIMESTAMP)
          .put("string", DataTypes.STRING)
          .put("boolean", DataTypes.BOOLEAN)
          .put("byte", DataTypes.BYTE)
          .put("short", DataTypes.SHORT)
          .put("integer", DataTypes.INTEGER)
          .put("long", DataTypes.LONG)
          .put("float", DataTypes.FLOAT)
          .put("double", DataTypes.DOUBLE)
          .put("ip", DataTypes.IP)
          .put("geo_point", DataTypes.GEO_POINT)
          .put("object", DataTypes.OBJECT)
          .put("nested", DataTypes.OBJECT)
          .build();

  public DocIndexMetaData(IndexMetaData metaData, TableIdent ident) throws IOException {
    this.ident = ident;
    this.metaData = metaData;
    this.isAlias = !metaData.getIndex().equals(ident.esName());
    this.numberOfShards = metaData.numberOfShards();
    final Settings settings = metaData.getSettings();
    this.numberOfReplicas = NumberOfReplicas.fromSettings(settings);
    this.aliases = ImmutableSet.copyOf(metaData.aliases().keys().toArray(String.class));
    this.defaultMappingMetaData = this.metaData.mappingOrDefault(Constants.DEFAULT_MAPPING_TYPE);
    if (defaultMappingMetaData == null) {
      this.defaultMappingMap = new HashMap<>();
    } else {
      this.defaultMappingMap = this.defaultMappingMetaData.sourceAsMap();
    }
    this.tableParameters = TableParameterInfo.tableParametersFromIndexMetaData(metaData);

    prepareCrateMeta();
  }

  @SuppressWarnings("unchecked")
  private static <T> T getNested(Map map, String key) {
    return (T) map.get(key);
  }

  private void prepareCrateMeta() {
    metaMap = getNested(defaultMappingMap, "_meta");
    if (metaMap != null) {
      indicesMap = getNested(metaMap, "indices");
      if (indicesMap == null) {
        indicesMap = ImmutableMap.of();
      }
      metaColumnsMap = getNested(metaMap, "columns");
      if (metaColumnsMap == null) {
        metaColumnsMap = ImmutableMap.of();
      }
      partitionedByList = getNested(metaMap, "partitioned_by");
      if (partitionedByList == null) {
        partitionedByList = ImmutableList.of();
      }
    } else {
      metaMap = new HashMap<>();
      indicesMap = new HashMap<>();
      metaColumnsMap = new HashMap<>();
      partitionedByList = ImmutableList.of();
    }
  }

  private void addPartitioned(ColumnIdent column, DataType type) {
    add(column, type, ColumnPolicy.DYNAMIC, ReferenceInfo.IndexType.NOT_ANALYZED, true);
  }

  private void add(ColumnIdent column, DataType type, ReferenceInfo.IndexType indexType) {
    add(column, type, ColumnPolicy.DYNAMIC, indexType, false);
  }

  private void add(
      ColumnIdent column,
      DataType type,
      ColumnPolicy columnPolicy,
      ReferenceInfo.IndexType indexType,
      boolean partitioned) {
    ReferenceInfo info = newInfo(column, type, columnPolicy, indexType);
    // don't add it if there is a partitioned equivalent of this column
    if (partitioned || !(partitionedBy != null && partitionedBy.contains(column))) {
      if (info.ident().isColumn()) {
        columnsBuilder.add(info);
      }
      referencesBuilder.put(info.ident().columnIdent(), info);
    }
    if (partitioned) {
      partitionedByColumnsBuilder.add(info);
    }
  }

  private ReferenceInfo newInfo(
      ColumnIdent column,
      DataType type,
      ColumnPolicy columnPolicy,
      ReferenceInfo.IndexType indexType) {
    RowGranularity granularity = RowGranularity.DOC;
    if (partitionedBy.contains(column)) {
      granularity = RowGranularity.PARTITION;
    }
    return new ReferenceInfo(
        new ReferenceIdent(ident, column), granularity, type, columnPolicy, indexType);
  }

  /**
   * extract dataType from given columnProperties
   *
   * @param columnProperties map of String to Object containing column properties
   * @return dataType of the column with columnProperties
   */
  public static DataType getColumnDataType(Map<String, Object> columnProperties) {
    DataType type;
    String typeName = (String) columnProperties.get("type");

    if (typeName == null) {
      if (columnProperties.containsKey("properties")) {
        type = DataTypes.OBJECT;
      } else {
        return DataTypes.NOT_SUPPORTED;
      }
    } else if (typeName.equalsIgnoreCase("array")) {

      Map<String, Object> innerProperties = getNested(columnProperties, "inner");
      DataType innerType = getColumnDataType(innerProperties);
      type = new ArrayType(innerType);
    } else {
      typeName = typeName.toLowerCase(Locale.ENGLISH);
      type = MoreObjects.firstNonNull(dataTypeMap.get(typeName), DataTypes.NOT_SUPPORTED);
    }
    return type;
  }

  private ReferenceInfo.IndexType getColumnIndexType(Map<String, Object> columnProperties) {
    String indexType = (String) columnProperties.get("index");
    String analyzerName = (String) columnProperties.get("analyzer");
    if (indexType != null) {
      if (indexType.equals(ReferenceInfo.IndexType.NOT_ANALYZED.toString())) {
        return ReferenceInfo.IndexType.NOT_ANALYZED;
      } else if (indexType.equals(ReferenceInfo.IndexType.NO.toString())) {
        return ReferenceInfo.IndexType.NO;
      } else if (indexType.equals(ReferenceInfo.IndexType.ANALYZED.toString())
          && analyzerName != null
          && !analyzerName.equals("keyword")) {
        return ReferenceInfo.IndexType.ANALYZED;
      }
    } // default indexType is analyzed so need to check analyzerName if indexType is null
    else if (analyzerName != null && !analyzerName.equals("keyword")) {
      return ReferenceInfo.IndexType.ANALYZED;
    }
    return ReferenceInfo.IndexType.NOT_ANALYZED;
  }

  private ColumnIdent childIdent(ColumnIdent ident, String name) {
    if (ident == null) {
      return new ColumnIdent(name);
    }
    if (ident.isColumn()) {
      return new ColumnIdent(ident.name(), name);
    } else {
      ImmutableList.Builder<String> builder = ImmutableList.builder();
      for (String s : ident.path()) {
        builder.add(s);
      }
      builder.add(name);
      return new ColumnIdent(ident.name(), builder.build());
    }
  }

  /** extracts index definitions as well */
  @SuppressWarnings("unchecked")
  private void internalExtractColumnDefinitions(
      ColumnIdent columnIdent, Map<String, Object> propertiesMap) {
    if (propertiesMap == null) {
      return;
    }

    for (Map.Entry<String, Object> columnEntry : propertiesMap.entrySet()) {
      Map<String, Object> columnProperties = (Map) columnEntry.getValue();
      DataType columnDataType = getColumnDataType(columnProperties);
      ColumnIdent newIdent = childIdent(columnIdent, columnEntry.getKey());

      columnProperties = furtherColumnProperties(columnProperties);
      ReferenceInfo.IndexType columnIndexType = getColumnIndexType(columnProperties);
      if (columnDataType == DataTypes.OBJECT
          || (columnDataType.id() == ArrayType.ID
              && ((ArrayType) columnDataType).innerType() == DataTypes.OBJECT)) {
        ColumnPolicy columnPolicy = ColumnPolicy.of(columnProperties.get("dynamic"));
        add(newIdent, columnDataType, columnPolicy, ReferenceInfo.IndexType.NO, false);

        if (columnProperties.get("properties") != null) {
          // walk nested
          internalExtractColumnDefinitions(
              newIdent, (Map<String, Object>) columnProperties.get("properties"));
        }
      } else if (columnDataType != DataTypes.NOT_SUPPORTED) {
        List<String> copyToColumns = getNested(columnProperties, "copy_to");

        // extract columns this column is copied to, needed for indices
        if (copyToColumns != null) {
          for (String copyToColumn : copyToColumns) {
            ColumnIdent targetIdent = ColumnIdent.fromPath(copyToColumn);
            IndexReferenceInfo.Builder builder = getOrCreateIndexBuilder(targetIdent);
            builder.addColumn(
                newInfo(newIdent, columnDataType, ColumnPolicy.DYNAMIC, columnIndexType));
          }
        }
        // is it an index?
        if (indicesMap.containsKey(newIdent.fqn())) {
          IndexReferenceInfo.Builder builder = getOrCreateIndexBuilder(newIdent);
          builder
              .indexType(columnIndexType)
              .ident(new ReferenceIdent(ident, newIdent))
              .analyzer((String) columnProperties.get("analyzer"));
        } else {
          add(newIdent, columnDataType, columnIndexType);
        }
      }
    }
  }

  /**
   * get the real column properties from a possible array mapping, keeping most of this stuff inside
   * "inner"
   */
  private Map<String, Object> furtherColumnProperties(Map<String, Object> columnProperties) {
    if (columnProperties.get("inner") != null) {
      return (Map<String, Object>) columnProperties.get("inner");
    } else {
      return columnProperties;
    }
  }

  private IndexReferenceInfo.Builder getOrCreateIndexBuilder(ColumnIdent ident) {
    IndexReferenceInfo.Builder builder = indicesBuilder.get(ident);
    if (builder == null) {
      builder = new IndexReferenceInfo.Builder();
      indicesBuilder.put(ident, builder);
    }
    return builder;
  }

  private ImmutableList<ColumnIdent> getPrimaryKey() {
    Map<String, Object> metaMap = getNested(defaultMappingMap, "_meta");
    if (metaMap != null) {
      ImmutableList.Builder<ColumnIdent> builder = ImmutableList.builder();
      Object pKeys = metaMap.get("primary_keys");
      if (pKeys != null) {
        if (pKeys instanceof String) {
          builder.add(ColumnIdent.fromPath((String) pKeys));
          return builder.build();
        } else if (pKeys instanceof Collection) {
          Collection keys = (Collection) pKeys;
          if (!keys.isEmpty()) {
            for (Object pkey : keys) {
              builder.add(ColumnIdent.fromPath(pkey.toString()));
            }
            return builder.build();
          }
        }
      }
    }
    if (getCustomRoutingCol() == null && partitionedByList.isEmpty()) {
      hasAutoGeneratedPrimaryKey = true;
      return ImmutableList.of(ID_IDENT);
    }
    return ImmutableList.of();
  }

  private ImmutableList<ColumnIdent> getPartitionedBy() {
    ImmutableList.Builder<ColumnIdent> builder = ImmutableList.builder();
    for (List<String> partitionedByInfo : partitionedByList) {
      builder.add(ColumnIdent.fromPath(partitionedByInfo.get(0)));
    }
    return builder.build();
  }

  private ColumnPolicy getColumnPolicy() {
    Object dynamic = getNested(defaultMappingMap, "dynamic");
    if (ColumnPolicy.STRICT.value().equals(String.valueOf(dynamic).toLowerCase(Locale.ENGLISH))) {
      return ColumnPolicy.STRICT;
    } else if (Booleans.isExplicitFalse(String.valueOf(dynamic))) {
      return ColumnPolicy.IGNORED;
    } else {
      return ColumnPolicy.DYNAMIC;
    }
  }

  private void createColumnDefinitions() {
    Map<String, Object> propertiesMap = getNested(defaultMappingMap, "properties");
    internalExtractColumnDefinitions(null, propertiesMap);
    extractPartitionedByColumns();
  }

  private ImmutableMap<ColumnIdent, IndexReferenceInfo> createIndexDefinitions() {
    ImmutableMap.Builder<ColumnIdent, IndexReferenceInfo> builder = ImmutableMap.builder();
    for (Map.Entry<ColumnIdent, IndexReferenceInfo.Builder> entry : indicesBuilder.entrySet()) {
      builder.put(entry.getKey(), entry.getValue().build());
    }
    indices = builder.build();
    return indices;
  }

  private void extractPartitionedByColumns() {
    for (Tuple<ColumnIdent, DataType> partitioned :
        PartitionedByMappingExtractor.extractPartitionedByColumns(partitionedByList)) {
      addPartitioned(partitioned.v1(), partitioned.v2());
    }
  }

  private ColumnIdent getCustomRoutingCol() {
    if (defaultMappingMetaData != null) {
      Map<String, Object> metaMap = getNested(defaultMappingMap, "_meta");
      if (metaMap != null) {
        String routingPath = (String) metaMap.get("routing");
        if (routingPath != null && !routingPath.equals(ID)) {
          return ColumnIdent.fromPath(routingPath);
        }
      }
    }
    return null;
  }

  private ColumnIdent getRoutingCol() {
    ColumnIdent col = getCustomRoutingCol();
    if (col != null) {
      return col;
    }
    if (primaryKey.size() == 1) {
      return primaryKey.get(0);
    }
    return ID_IDENT;
  }

  public DocIndexMetaData build() {
    partitionedBy = getPartitionedBy();
    columnPolicy = getColumnPolicy();
    createColumnDefinitions();
    indices = createIndexDefinitions();
    columns = ImmutableList.copyOf(columnsBuilder.build());
    partitionedByColumns = partitionedByColumnsBuilder.build();

    for (Tuple<ColumnIdent, ReferenceInfo> sysColumn : DocSysColumns.forTable(ident)) {
      referencesBuilder.put(sysColumn.v1(), sysColumn.v2());
    }
    references = referencesBuilder.build();
    primaryKey = getPrimaryKey();
    routingCol = getRoutingCol();
    return this;
  }

  public ImmutableMap<ColumnIdent, ReferenceInfo> references() {
    return references;
  }

  public ImmutableList<ReferenceInfo> columns() {
    return columns;
  }

  public ImmutableMap<ColumnIdent, IndexReferenceInfo> indices() {
    return indices;
  }

  public ImmutableList<ReferenceInfo> partitionedByColumns() {
    return partitionedByColumns;
  }

  public ImmutableList<ColumnIdent> primaryKey() {
    return primaryKey;
  }

  public ColumnIdent routingCol() {
    return routingCol;
  }

  /**
   * Returns true if the schema of this and <code>other</code> is the same, this includes the table
   * name, as this is reflected in the ReferenceIdents of the columns.
   */
  public boolean schemaEquals(DocIndexMetaData other) {
    if (this == other) return true;
    if (other == null) return false;

    // TODO: when analyzers are exposed in the info, equality has to be checked on them
    // see: TransportSQLActionTest.testSelectTableAliasSchemaExceptionColumnDefinition
    if (columns != null ? !columns.equals(other.columns) : other.columns != null) return false;
    if (primaryKey != null ? !primaryKey.equals(other.primaryKey) : other.primaryKey != null)
      return false;
    if (indices != null ? !indices.equals(other.indices) : other.indices != null) return false;
    if (references != null ? !references.equals(other.references) : other.references != null)
      return false;
    if (routingCol != null ? !routingCol.equals(other.routingCol) : other.routingCol != null)
      return false;

    return true;
  }

  protected DocIndexMetaData merge(
      DocIndexMetaData other,
      TransportPutIndexTemplateAction transportPutIndexTemplateAction,
      boolean thisIsCreatedFromTemplate)
      throws IOException {
    if (schemaEquals(other)) {
      return this;
    } else if (thisIsCreatedFromTemplate) {
      if (this.references.size() < other.references.size()) {
        // this is older, update template and return other
        // settings in template are always authoritative for table information about
        // number_of_shards and number_of_replicas
        updateTemplate(other, transportPutIndexTemplateAction, this.metaData.settings());
        // merge the new mapping with the template settings
        return new DocIndexMetaData(
                IndexMetaData.builder(other.metaData).settings(this.metaData.settings()).build(),
                other.ident)
            .build();
      } else if (references().size() == other.references().size()
          && !references().keySet().equals(other.references().keySet())) {
        XContentHelper.update(defaultMappingMap, other.defaultMappingMap, false);
        // update the template with new information
        updateTemplate(this, transportPutIndexTemplateAction, this.metaData.settings());
        return this;
      }
      // other is older, just return this
      return this;
    } else {
      throw new TableAliasSchemaException(other.ident.name());
    }
  }

  private void updateTemplate(
      DocIndexMetaData md,
      TransportPutIndexTemplateAction transportPutIndexTemplateAction,
      Settings updateSettings) {
    String templateName = PartitionName.templateName(ident.schema(), ident.name());
    PutIndexTemplateRequest request =
        new PutIndexTemplateRequest(templateName)
            .mapping(Constants.DEFAULT_MAPPING_TYPE, md.defaultMappingMap)
            .create(false)
            .settings(updateSettings)
            .template(templateName + "*");
    for (String alias : md.aliases()) {
      request = request.alias(new Alias(alias));
    }
    transportPutIndexTemplateAction.execute(request);
  }

  /** @return the name of the underlying index even if this table is referenced by alias */
  public String concreteIndexName() {
    return metaData.index();
  }

  public boolean isAlias() {
    return isAlias;
  }

  public Set<String> aliases() {
    return aliases;
  }

  public boolean hasAutoGeneratedPrimaryKey() {
    return hasAutoGeneratedPrimaryKey;
  }

  public int numberOfShards() {
    return numberOfShards;
  }

  public BytesRef numberOfReplicas() {
    return numberOfReplicas;
  }

  public ImmutableList<ColumnIdent> partitionedBy() {
    return partitionedBy;
  }

  public ColumnPolicy columnPolicy() {
    return columnPolicy;
  }

  public ImmutableMap<String, Object> tableParameters() {
    return tableParameters;
  }

  private ImmutableMap<ColumnIdent, String> getAnalyzers(
      ColumnIdent columnIdent, Map<String, Object> propertiesMap) {
    ImmutableMap.Builder<ColumnIdent, String> builder = ImmutableMap.builder();
    for (Map.Entry<String, Object> columnEntry : propertiesMap.entrySet()) {
      Map<String, Object> columnProperties = (Map) columnEntry.getValue();
      DataType columnDataType = getColumnDataType(columnProperties);
      ColumnIdent newIdent = childIdent(columnIdent, columnEntry.getKey());
      columnProperties = furtherColumnProperties(columnProperties);
      if (columnDataType == DataTypes.OBJECT
          || (columnDataType.id() == ArrayType.ID
              && ((ArrayType) columnDataType).innerType() == DataTypes.OBJECT)) {
        if (columnProperties.get("properties") != null) {
          builder.putAll(
              getAnalyzers(newIdent, (Map<String, Object>) columnProperties.get("properties")));
        }
      }
      String analyzer = (String) columnProperties.get("analyzer");
      if (analyzer != null) {
        builder.put(newIdent, analyzer);
      }
    }
    return builder.build();
  }

  public ImmutableMap<ColumnIdent, String> analyzers() {
    Map<String, Object> propertiesMap = getNested(defaultMappingMap, "properties");
    if (propertiesMap == null) {
      return ImmutableMap.of();
    } else {
      return getAnalyzers(null, propertiesMap);
    }
  }
}