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(); }
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); } }
public ImmutableMap<ColumnIdent, String> analyzers() { Map<String, Object> propertiesMap = getNested(defaultMappingMap, "properties"); if (propertiesMap == null) { return ImmutableMap.of(); } else { return getAnalyzers(null, propertiesMap); } }
/** * 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; }
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(); } })); }
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(); } }
/** * 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 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 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); } } }
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); } }
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); } }
/** @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(); } }
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; }
/** * 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; }
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); } } }