/* * JBoss, Home of Professional Open Source. * * See the LEGAL.txt file distributed with this work for information regarding copyright ownership and licensing. * * See the AUTHORS.txt file distributed with this work for a full listing of individual contributors. */ package org.teiid.designer.metadata.runtime.impl; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.teiid.core.designer.id.UUID; import org.teiid.core.designer.util.CoreArgCheck; import org.teiid.core.designer.util.CoreStringUtil; import org.teiid.designer.core.container.EObjectFinder; import org.teiid.designer.core.index.EntryResult; import org.teiid.designer.core.index.IEntryResult; import org.teiid.designer.core.index.IIndexConstants; import org.teiid.designer.core.index.Index; import org.teiid.designer.core.index.IndexConstants; import org.teiid.designer.metadata.runtime.AnnotationRecord; import org.teiid.designer.metadata.runtime.ColumnRecord; import org.teiid.designer.metadata.runtime.ColumnSetRecord; import org.teiid.designer.metadata.runtime.DatatypeRecord; import org.teiid.designer.metadata.runtime.FileRecord; import org.teiid.designer.metadata.runtime.ForeignKeyRecord; import org.teiid.designer.metadata.runtime.MetadataRecord; import org.teiid.designer.metadata.runtime.ModelRecord; import org.teiid.designer.metadata.runtime.ProcedureParameterRecord; import org.teiid.designer.metadata.runtime.ProcedureRecord; import org.teiid.designer.metadata.runtime.PropertyRecord; import org.teiid.designer.metadata.runtime.TableRecord; import org.teiid.designer.metadata.runtime.TransformationRecord; import org.teiid.designer.metadata.runtime.UniqueKeyRecord; import org.teiid.designer.metadata.runtime.VdbRecord; /** * RuntimeAdapter * * @since 8.0 */ public class RecordFactory { public static final int INDEX_RECORD_BLOCK_SIZE = IIndexConstants.BLOCK_SIZE - 32; /** * The version number associated with any index records prior to the point * when version information was encoded in newly created records */ public static final int NONVERSIONED_RECORD_INDEX_VERSION = 0; /** * The version number that is associated with the change made to change the list * delimiter from {@link org.teiid.designer.core.index.IndexConstants.RECORD_STRING#LIST_DELIMITER_OLD} * to {@link org.teiid.designer.core.index.IndexConstants.RECORD_STRING#LIST_DELIMITER} and also the * property delimiter was changed from {@link org.teiid.designer.core.index.IndexConstants.RECORD_STRING#PROP_DELIMITER_OLD} * to {@link org.teiid.designer.core.index.IndexConstants.RECORD_STRING#PROP_DELIMITER}. Added 07/22/2004. * @release 4.1.1 */ public static final int DELIMITER_INDEX_VERSION = 1; /** * The version number that is associated with the change made to add materialization * property on tables. Added 08/18/2004. * @release 4.2 */ public static final int TABLE_MATERIALIZATION_INDEX_VERSION = 2; /** * The version number that is associated with the change made to add native type * property on columns. Added 08/24/2004. * @release 4.2 */ public static final int COLUMN_NATIVE_TYPE_INDEX_VERSION = 3; /** * The version number that is associated with the change made to add an input parameter * flag on columns. The flag is used to indicate if an element for a virtual table * represents an input parameter. This change was made to support the Procedural-Relational * Mapping project. Added 09/29/2004. * @release 4.2 */ public static final int COLUMN_INPUT_PARAMETER_FLAG_INDEX_VERSION = 4; /** * The version number that is associated with the change made to remove property value * pairs from the annotation records any properties on annotations would now be indexed * as part of the properties index. Added 12/14/2004. * @release 4.2 */ public static final int ANNOTATION_TAGS_INDEX_VERSION = 5; /** * The version number that is associated with the change made to add uuid for the * transformation mapping root on the transformation records, uuids would now be indexed * as part of the transformation index. Added 1/13/2005. * @release 4.2 */ public static final int TRANSFORMATION_UUID_INDEX_VERSION = 6; /** * The version number that is associated with the change made to add count of null and * distinct values for columns on the column records 7/8/2005. * @release 4.2 */ public static final int COLUMN_NULL_DISTINCT_INDEX_VERSION = 7; /** * The version number that is associated with the change made to add * primitive type ID on datatype records 02/28/2006. * @release 5.0 */ public static final int PRIMITIVE_TYPE_ID_INDEX_VERSION = 8; /** * The version number that is associated with the change made to add * an update count to physical stored and XQuery procedures 04/29/2008. * @release 5.0 */ public static final int PROCEDURE_UPDATE_COUNT_VERSION = 9; /** * The version number that is associated with the change made to set -1 as the default * table cardinality and that 0 is an allowed value 01/29/2013 (BML). * @release 5.0 */ public static final int TABLE_CARDINALITY_MINUS_ONE_VERSION = 10; /** * The version number that is encoded with all newly created index records */ public static final int CURRENT_INDEX_VERSION = TABLE_CARDINALITY_MINUS_ONE_VERSION; // ================================================================================== // P U B L I C M E T H O D S // ================================================================================== /** * Return a collection of {@link org.teiid.designer.metadata.runtime.MetadataRecord} * instances for the result obtained from executing <code>queryEntriesMatching</code> * method on the {@link Index} * @param queryResult * @param container Container reference to be set on the record */ public static Collection getMetadataRecord(final IEntryResult[] queryResult, final EObjectFinder container) { final Collection records = new ArrayList(queryResult.length); for (int i = 0; i < queryResult.length; i++) { final MetadataRecord record = getMetadataRecord(queryResult[i], container); if (record != null) { records.add(record); } } return records; } /** * Return a collection of {@link org.teiid.designer.metadata.runtime.MetadataRecord} * instances for the result obtained from executing <code>queryEntriesMatching</code> * method on the {@link Index} * @param queryResult * @param container Container reference to be set on the record */ public static Collection getMetadataRecord(final IEntryResult[] queryResult) { final Collection records = new ArrayList(queryResult.length); for (int i = 0; i < queryResult.length; i++) { final MetadataRecord record = getMetadataRecord(queryResult[i]); if (record != null) { records.add(record); } } return records; } /** * Return the {@link org.teiid.designer.metadata.runtime.MetadataRecord} * instances for specified IEntryResult. * @param entryResult */ private static MetadataRecord getMetadataRecord(final char[] record) { if (record == null || record.length == 0) { return null; } switch (record[0]) { case IndexConstants.RECORD_TYPE.MODEL: return createModelRecord(record); case IndexConstants.RECORD_TYPE.TABLE: return createTableRecord(record); case IndexConstants.RECORD_TYPE.JOIN_DESCRIPTOR: return null; case IndexConstants.RECORD_TYPE.CALLABLE: return createProcedureRecord(record); case IndexConstants.RECORD_TYPE.CALLABLE_PARAMETER: return createProcedureParameterRecord(record); case IndexConstants.RECORD_TYPE.COLUMN: return createColumnRecord(record); case IndexConstants.RECORD_TYPE.ACCESS_PATTERN: case IndexConstants.RECORD_TYPE.INDEX: case IndexConstants.RECORD_TYPE.RESULT_SET: return createColumnSetRecord(record); case IndexConstants.RECORD_TYPE.UNIQUE_KEY: case IndexConstants.RECORD_TYPE.PRIMARY_KEY: return createUniqueKeyRecord(record); case IndexConstants.RECORD_TYPE.FOREIGN_KEY: return createForeignKeyRecord(record); case IndexConstants.RECORD_TYPE.DATATYPE: return createDatatypeRecord(record); case IndexConstants.RECORD_TYPE.SELECT_TRANSFORM: case IndexConstants.RECORD_TYPE.INSERT_TRANSFORM: case IndexConstants.RECORD_TYPE.UPDATE_TRANSFORM: case IndexConstants.RECORD_TYPE.DELETE_TRANSFORM: case IndexConstants.RECORD_TYPE.MAPPING_TRANSFORM: case IndexConstants.RECORD_TYPE.PROC_TRANSFORM: return createTransformationRecord(record); case IndexConstants.RECORD_TYPE.VDB_ARCHIVE: return createVdbRecord(record); case IndexConstants.RECORD_TYPE.ANNOTATION: return createAnnotationRecord(record); case IndexConstants.RECORD_TYPE.PROPERTY: return createPropertyRecord(record); case IndexConstants.RECORD_TYPE.FILE: return createFileRecord(record); default: throw new IllegalArgumentException("Invalid record type for creating MetadataRecord "+record[0]); //$NON-NLS-1$ } } /** * Return the {@link org.teiid.designer.metadata.runtime.MetadataRecord} * instances for specified IEntryResult. * @param entryResult */ public static MetadataRecord getMetadataRecord(final IEntryResult queryResult) { return getMetadataRecord(queryResult, null); } /** * Return the {@link org.teiid.designer.metadata.runtime.MetadataRecord} * instances for specified IEntryResult. * @param entryResult * @param container Container reference to be set on the record */ public static MetadataRecord getMetadataRecord(final IEntryResult queryResult, final EObjectFinder container) { MetadataRecord record = getMetadataRecord(queryResult.getWord()); if(record instanceof AbstractMetadataRecord) { ((AbstractMetadataRecord)record).setEObjectFinder(container); } return record; } /** * Append the specified IEntryResult[] to the IEntryResult * to create a single result representing an index entry that * was split across multiple index records. * @param result * @param continuationResults * @param blockSize */ public static IEntryResult joinEntryResults(final IEntryResult result, final IEntryResult[] continuationResults, final int blockSize) { CoreArgCheck.isNotNull(result); // If the IEntryResult is not continued on another record, return the original char[] baseResult = result.getWord(); if (baseResult.length < blockSize || baseResult[blockSize-1] != IndexConstants.RECORD_TYPE.RECORD_CONTINUATION) { return result; } // Extract the UUID string from the original result String baseStr = new String(baseResult); String objectID = extractUUIDString(result); // Create and initialize a StringBuffer to store the concatenated result StringBuffer sb = new StringBuffer(); sb.append(baseStr.substring(0,blockSize-1)); // Append the continuation results onto the original - // assumes the IEntryResult[] are in ascending order of segment number final IEntryResult[] sortedResults = sortContinuationResults(objectID,continuationResults); for (int i = 0; i < sortedResults.length; i++) { char[] continuation = sortedResults[i].getWord(); int segNumber = getContinuationSegmentNumber(objectID,sortedResults[i]); int beginIndex = objectID.length() + Integer.toString(segNumber).length() + 5; for (int j = beginIndex; j < continuation.length; j++) { if (j < blockSize-1) { sb.append(continuation[j]); } } } return new EntryResult(sb.toString().toCharArray(),result.getFileReferences()); } private static IEntryResult[] sortContinuationResults(final String objectID, final IEntryResult[] continuationResults) { // If the array length is less than 10, then we should be able to safely // assume the IEntryResult[] are in ascending order of segment count if (continuationResults.length < 10) { return continuationResults; } // If the number of continuation records is 10 or greater then we need to sort them // by segment count since continuation records 10-19 will appear before records 1-9 // in the array final IEntryResult[] sortedResults = new IEntryResult[continuationResults.length]; for (int i = 0; i < continuationResults.length; i++) { int segNumber = getContinuationSegmentNumber(objectID,continuationResults[i]); sortedResults[segNumber-1] = continuationResults[i]; } return sortedResults; } public static int getContinuationSegmentNumber(final String objectID, final IEntryResult continuationResult) { // The header portion of the continuation record is of the form: // RECORD_CONTINUATION|objectID|segmentCount| char[] record = continuationResult.getWord(); int segNumber = -1; int index = objectID.length() + 4; if (record[index+1] == IndexConstants.RECORD_STRING.RECORD_DELIMITER) { // segment count < 10 segNumber = Character.getNumericValue(record[index]); } else if (record[index+2] == IndexConstants.RECORD_STRING.RECORD_DELIMITER) { // 9 < segment count < 100 char[] temp = new char[] {record[index], record[index+1]}; String segCount = new String(temp); segNumber = Integer.parseInt(segCount); } else if (record[index+3] == IndexConstants.RECORD_STRING.RECORD_DELIMITER) { // 99 < segment count < 1000 char[] temp = new char[] {record[index], record[index+1], record[index+2]}; String segCount = new String(temp); segNumber = Integer.parseInt(segCount); } return segNumber; } /** * Extract the UUID string from the IEntryResult * @param result */ public static String extractUUIDString(final IEntryResult result) { CoreArgCheck.isNotNull(result); char[] word = result.getWord(); String baseStr = new String(word); int beginIndex = baseStr.indexOf(UUID.PROTOCOL); int endIndex = word.length; CoreArgCheck.isNonNegative(beginIndex); for (int i = beginIndex; i < word.length; i++) { if (word[i] == IndexConstants.RECORD_STRING.RECORD_DELIMITER) { endIndex = i; break; } } CoreArgCheck.isTrue(beginIndex < endIndex, "begin index should be less than end index"); return baseStr.substring(beginIndex,endIndex); } // ================================================================================== // P R O T E C T E D M E T H O D S // ================================================================================== /** * Create a ModelRecord instance from the specified index record */ public static ModelRecord createModelRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ModelRecordImpl model = new ModelRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); model.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(model, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token is the max set size model.setMaxSetSize( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the model type model.setModelType( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the primary metamodel Uri model.setPrimaryMetamodelUri(getObjectValue((String)tokens.get(tokenIndex++))); // The next token are the supports flags char[] supportFlags = ((String)tokens.get(tokenIndex++)).toCharArray(); model.setVisible(getBooleanValue(supportFlags[0])); model.setSupportsDistinct(getBooleanValue(supportFlags[1])); model.setSupportsJoin(getBooleanValue(supportFlags[2])); model.setSupportsOrderBy(getBooleanValue(supportFlags[3])); model.setSupportsOuterJoin(getBooleanValue(supportFlags[4])); model.setSupportsWhereAll(getBooleanValue(supportFlags[5])); // The next tokens are footer values - the footer will contain the version number for the index record setRecordFooterValues(model, tokens, tokenIndex); return model; } /** * Create a ModelRecord instance from the specified index record */ public static VdbRecord createVdbRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final VdbRecordImpl vdb = new VdbRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); vdb.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(vdb, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token is the version vdb.setVersion(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the identifier vdb.setIdentifier(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the producerName vdb.setProducerName(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the producerVersion vdb.setProducerVersion(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the provider vdb.setProvider(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the timeLastChanged vdb.setTimeLastChanged(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the timeLastProduced vdb.setTimeLastProduced(getObjectValue((String)tokens.get(tokenIndex++))); // The next token are the UUIDs for the models in the VDB archive List uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); vdb.setModelIDs(uuids); // The next token is the description vdb.setDescription(getObjectValue((String)tokens.get(tokenIndex++))); // The next tokens are footer values setRecordFooterValues(vdb, tokens, tokenIndex); return vdb; } /** * Create a TransformationRecord instance from the specified index record */ public static TransformationRecord createTransformationRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final TransformationRecordImpl transform = new TransformationRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); transform.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; char recordType = ((String)tokens.get(tokenIndex++)).charAt(0); // The next token is the transformation type transform.setTransformationType(getObjectValue(transform.getTransformTypeForRecordType(recordType))); // The next token is the name of the transformed object transform.setFullName(getObjectValue(((String)tokens.get(tokenIndex++)))); // The next token is the UUID of the transformed object transform.setTransformedObjectID(getObjectValue((String)tokens.get(tokenIndex++))); // The next token is the UUID of the transformation object if(includeTransformationUUID(indexVersion)) { transform.setUUID(getObjectValue(((String)tokens.get(tokenIndex++)))); } // The next token is the transformation definition transform.setTransformation(getObjectValue((String)tokens.get(tokenIndex++))); // The next token are the list of bindings List bindings = getStrings((String)tokens.get(tokenIndex++), indexVersion); transform.setBindings(bindings); // The next token are the list of schemaPaths List schemaPaths = getStrings((String)tokens.get(tokenIndex++), indexVersion); transform.setSchemaPaths(schemaPaths); // The next tokens are footer values setRecordFooterValues(transform, tokens, tokenIndex); return transform; } /** * Create a TableRecord instance from the specified index record */ public static TableRecord createTableRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final TableRecordImpl table = new TableRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); table.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(table, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token is the cardinality table.setCardinality( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the tableType table.setTableType( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token are the supports flags char[] supportFlags = ((String)tokens.get(tokenIndex++)).toCharArray(); table.setVirtual(getBooleanValue(supportFlags[0])); table.setSystem(getBooleanValue(supportFlags[1])); table.setSupportsUpdate(getBooleanValue(supportFlags[2])); if(includeMaterializationFlag(indexVersion)) { table.setMaterialized(getBooleanValue(supportFlags[3])); } // The next token are the UUIDs for the column references List uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); table.setColumnIDs(uuids); // The next token is the UUID of the primary key table.setPrimaryKeyID(tokens.get(tokenIndex++)); // The next token are the UUIDs for the foreign key references uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); table.setForeignKeyIDs(uuids); // The next token are the UUIDs for the index references uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); table.setIndexIDs(uuids); // The next token are the UUIDs for the unique key references uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); table.setUniqueKeyIDs(uuids); // The next token are the UUIDs for the access pattern references uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); table.setAccessPatternIDs(uuids); if(includeMaterializationFlag(indexVersion)) { // The next token are the UUIDs for the materialized table ID table.setMaterializedTableID(tokens.get(tokenIndex++)); // The next token are the UUID for the materialized stage table ID table.setMaterializedStageTableID(tokens.get(tokenIndex++)); } // Initializes the temp table value if( table.getTableType() == TableRecord.Type.TemporaryTable.ordinal() ) { table.setTempTable(true); } // The next tokens are footer values setRecordFooterValues(table, tokens, tokenIndex); return table; } /** * Create a ColumnRecord instance from the specified index record */ public static ColumnRecord createColumnRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ColumnRecordImpl column = new ColumnRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); column.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(column, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token are the supports flags char[] supportFlags = ((String)tokens.get(tokenIndex++)).toCharArray(); column.setSelectable(getBooleanValue(supportFlags[0])); column.setUpdatable(getBooleanValue(supportFlags[1])); column.setAutoIncrementable(getBooleanValue(supportFlags[2])); column.setCaseSensitive(getBooleanValue(supportFlags[3])); column.setSigned(getBooleanValue(supportFlags[4])); column.setCurrency(getBooleanValue(supportFlags[5])); column.setFixedLength(getBooleanValue(supportFlags[6])); if (includeInputParameterFlag(indexVersion)) { column.setTransformationInputParameter(getBooleanValue(supportFlags[7])); } // The next token is the search type column.setNullType( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the search type column.setSearchType( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the length column.setLength( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the scale column.setScale( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the precision column.setPrecision( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the precision column.setPosition( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the charOctetLength column.setCharOctetLength( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the radix column.setRadix( Integer.parseInt((String)tokens.get(tokenIndex++)) ); if (includeColumnNullDistinctValues(indexVersion)) { // The next token is the distinct value column.setDistinctValues(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the null value column.setNullValues(Integer.parseInt((String)tokens.get(tokenIndex++)) ); } // The next token is the min value column.setMinValue( getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the max value column.setMaxValue( getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the format value column.setFormat( getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the runtime type column.setRuntimeType( getObjectValue((String)tokens.get(tokenIndex++)) ); if(includeColumnNativeType(indexVersion)) { // The next token is the native type column.setNativeType( getObjectValue((String)tokens.get(tokenIndex++)) ); } // The next token is the datatype ObjectID column.setDatatypeUUID( getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the default value column.setDefaultValue( getObjectValue((String)tokens.get(tokenIndex++)) ); // The next tokens are footer values setRecordFooterValues(column, tokens, tokenIndex); return column; } /** * Create a ColumnSetRecord instance from the specified index record */ public static ColumnSetRecord createColumnSetRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ColumnSetRecordImpl columnSet = new ColumnSetRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); columnSet.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(columnSet, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token are the UUIDs for the column references List uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); columnSet.setColumnIDs(uuids); // The next tokens are footer values setRecordFooterValues(columnSet, tokens, tokenIndex); return columnSet; } /** * Create a ForeignKeyRecord instance from the specified index record */ public static ForeignKeyRecord createForeignKeyRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ForeignKeyRecordImpl fkRecord = new ForeignKeyRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); fkRecord.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(fkRecord, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token are the UUIDs for the column references List uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); fkRecord.setColumnIDs(uuids); // The next token is the UUID of the unique key fkRecord.setUniqueKeyID(getObjectValue((String)tokens.get(tokenIndex++))); // The next tokens are footer values setRecordFooterValues(fkRecord, tokens, tokenIndex); return fkRecord; } /** * Create a UniqueKeyRecord instance from the specified index record */ public static UniqueKeyRecord createUniqueKeyRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final UniqueKeyRecordImpl ukRecord = new UniqueKeyRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); ukRecord.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(ukRecord, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token are the UUIDs for the column references List columnUUIDs = getIDs((String)tokens.get(tokenIndex++), indexVersion); ukRecord.setColumnIDs(columnUUIDs); // The next token are the UUIDs for the foreign key references List fkUUIDs = getIDs((String)tokens.get(tokenIndex++), indexVersion); ukRecord.setForeignKeyIDs(fkUUIDs); // The next tokens are footer values setRecordFooterValues(ukRecord, tokens, tokenIndex); return ukRecord; } /** * Create a DatatypeRecord instance from the specified index record */ public static DatatypeRecord createDatatypeRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final DatatypeRecordImpl dt = new DatatypeRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); dt.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; // Set the record type dt.setRecordType(((String)tokens.get(tokenIndex++)).toCharArray()[0]); // Set the datatype and basetype identifiers dt.setDatatypeID(getObjectValue((String)tokens.get(tokenIndex++))); dt.setBasetypeID(getObjectValue((String)tokens.get(tokenIndex++))); // Set the fullName/objectID/nameInSource dt.setFullName((String)tokens.get(tokenIndex++)); dt.setUUID(getObjectValue((String)tokens.get(tokenIndex++))); dt.setNameInSource(getObjectValue((String)tokens.get(tokenIndex++))); // Set the variety type and its properties dt.setVarietyType( Short.parseShort((String)tokens.get(tokenIndex++)) ); List props = getIDs((String)tokens.get(tokenIndex++), indexVersion); dt.setVarietyProps(props); // Set the runtime and java class names dt.setRuntimeTypeName(getObjectValue((String)tokens.get(tokenIndex++))); dt.setJavaClassName(getObjectValue((String)tokens.get(tokenIndex++))); // Set the datatype type dt.setType( Short.parseShort((String)tokens.get(tokenIndex++)) ); // Set the search type dt.setSearchType( Short.parseShort((String)tokens.get(tokenIndex++)) ); // Set the null type dt.setNullType( Short.parseShort((String)tokens.get(tokenIndex++)) ); // Set the boolean flags char[] booleanValues = ((String)tokens.get(tokenIndex++)).toCharArray(); dt.setSigned(getBooleanValue(booleanValues[0])); dt.setAutoIncrement(getBooleanValue(booleanValues[1])); dt.setCaseSensitive(getBooleanValue(booleanValues[2])); // Append the length dt.setLength( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // Append the precision length dt.setPrecisionLength( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // Append the scale dt.setScale( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // Append the radix dt.setRadix( Integer.parseInt((String)tokens.get(tokenIndex++)) ); // Set the primitive type identifier if (includePrimitiveTypeIdValue(indexVersion)) { // The next token is the primitive type identifier dt.setPrimitiveTypeID(getObjectValue((String)tokens.get(tokenIndex++))); } // The next tokens are footer values setRecordFooterValues(dt, tokens, tokenIndex); return dt; } /** * Create a ProcedureRecord instance from the specified index record */ public static ProcedureRecord createProcedureRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ProcedureRecordImpl procRd = new ProcedureRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); procRd.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; // Set the record type setRecordHeaderValues(procRd, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // Set the boolean flags char[] booleanValues = ((String)tokens.get(tokenIndex++)).toCharArray(); // flag indicating if the procedure is a function procRd.setFunction(getBooleanValue(booleanValues[0])); // flag indicating if the procedure is virtual procRd.setVirtual(getBooleanValue(booleanValues[1])); // The next token are the UUIDs for the param references List uuids = getIDs((String)tokens.get(tokenIndex++), indexVersion); procRd.setParameterIDs(uuids); // The next token is the UUID of the resultSet object procRd.setResultSetID(getObjectValue((String)tokens.get(tokenIndex++))); if (includeProcedureUpdateCount(indexVersion)) { procRd.setUpdateCount(Integer.parseInt((String)tokens.get(tokenIndex++))); } // The next tokens are footer values setRecordFooterValues(procRd, tokens, tokenIndex); return procRd; } /** * Create a ProcedureParameterRecord instance from the specified index record * header|defaultValue|dataType|length|radix|scale|nullType|precision|paramType|footer| */ public static ProcedureParameterRecord createProcedureParameterRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final ProcedureParameterRecordImpl paramRd = new ProcedureParameterRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); paramRd.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; // Set the record type setRecordHeaderValues(paramRd, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); // The next token is the default value of the parameter paramRd.setDefaultValue(getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the runtime type paramRd.setRuntimeType(getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the uuid paramRd.setDatatypeUUID(getObjectValue((String)tokens.get(tokenIndex++)) ); // The next token is the length paramRd.setLength(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the radix paramRd.setRadix(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the scale paramRd.setScale(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the null type paramRd.setNullType(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the precision paramRd.setPrecision(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is the position paramRd.setPosition(Integer.parseInt((String)tokens.get(tokenIndex++)) ); // The next token is parameter type paramRd.setType(Integer.parseInt((String)tokens.get(tokenIndex++))); // The next token is flag for parameter optional prop char[] flags = ((String)tokens.get(tokenIndex++)).toCharArray(); paramRd.setOptional(getBooleanValue(flags[0])); // The next tokens are footer values setRecordFooterValues(paramRd, tokens, tokenIndex); return paramRd; } /** * Create a AnnotationRecord instance from the specified index record */ public static AnnotationRecord createAnnotationRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final AnnotationRecordImpl annotation = new AnnotationRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); annotation.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; setRecordHeaderValues(annotation, (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++), (String)tokens.get(tokenIndex++)); if(includeAnnotationProperties(indexVersion)) { // The next token are the properties, ignore it not going to be read any way tokenIndex++; } // The next token is the description annotation.setDescription((String)tokens.get(tokenIndex++)); // The next tokens are footer values setRecordFooterValues(annotation, tokens, tokenIndex); return annotation; } /** * Create a PropertyRecord instance from the specified index record */ public static PropertyRecord createPropertyRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final PropertyRecordImpl property = new PropertyRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); property.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; // The next token is the record type String recordType = (String)tokens.get(tokenIndex++); property.setRecordType(recordType.toCharArray()[0]); // The next token is the object ID String objectID = (String)tokens.get(tokenIndex++); property.setUUID(getObjectValue(objectID)); // The next token is the property name property.setPropertyName( (String)tokens.get(tokenIndex++) ); // The next token is the property value property.setPropertyValue((String)tokens.get(tokenIndex++)); // for newer records if(!includeAnnotationProperties(indexVersion)) { // The next token is extension boolean char isExtension = ((String)tokens.get(tokenIndex++)).charAt(0); property.setExtension(getBooleanValue(isExtension)); } // The next tokens are footer values setRecordFooterValues(property, tokens, tokenIndex); return property; } /** * Create a FileRecord instance from the specified index record */ public static FileRecord createFileRecord(final char[] record) { final String str = new String(record); final List tokens = CoreStringUtil.split(str,String.valueOf(IndexConstants.RECORD_STRING.RECORD_DELIMITER)); final FileRecordImpl file = new FileRecordImpl(); // Extract the index version information from the record int indexVersion = getIndexVersion(record); file.setIndexVersion(indexVersion); // The tokens are the standard header values int tokenIndex = 0; // The next token is the record type String recordType = (String)tokens.get(tokenIndex++); file.setRecordType(recordType.toCharArray()[0]); // The next token is the relative path to the file in vdb file.setPathInVdb((String)tokens.get(tokenIndex++) ); return file; } /** * Search for and return the version number associated with this record. * If no version information is found encoded in the record then the * version number of NONVERSIONED_RECORD_INDEX_VERSION will be returned. * @param record * @since 4.2 */ public static int getIndexVersion(final char[] record) { CoreArgCheck.isNotNull(record); int endIndex = record.length; int beginIndex = (endIndex - 6 > 0 ? endIndex - 6 : 1); int version = NONVERSIONED_RECORD_INDEX_VERSION; for (int i = beginIndex; i < endIndex; i++) { if (record[i] == IndexConstants.RECORD_STRING.INDEX_VERSION_MARKER) { char versionPart1 = record[i+1]; char versionPart2 = record[i+2]; if (Character.isDigit(versionPart1) && Character.isDigit(versionPart2)){ version = Character.digit(versionPart1, 10) * 10 + Character.digit(versionPart2, 10); } } } return version; } public static String getObjectValue(final String str) { if (str != null && str.length() == 1 && str.charAt(0) == IndexConstants.RECORD_STRING.SPACE) { return null; } return str; } public static boolean getBooleanValue(final char b) { if (b == IndexConstants.RECORD_STRING.TRUE) { return true; } return false; } public static List getIDs(final String values, final int indexVersionNumber) { if (CoreStringUtil.isEmpty(values)) { return Collections.EMPTY_LIST; } if (values.length() == 1 && values.charAt(0) == IndexConstants.RECORD_STRING.SPACE) { return Collections.EMPTY_LIST; } final char listDelimiter = getListDelimiter(indexVersionNumber); final List tokens = CoreStringUtil.split(values,String.valueOf(listDelimiter)); final List result = new ArrayList(tokens.size()); for (Iterator iter = tokens.iterator(); iter.hasNext();) { String token = getObjectValue((String)iter.next()); if (token != null) { result.add(token); } } return result; } public static List getStrings(final String values, final int indexVersionNumber) { if (CoreStringUtil.isEmpty(values)) { return Collections.EMPTY_LIST; } if (values.length() == 1 && values.charAt(0) == IndexConstants.RECORD_STRING.SPACE) { return Collections.EMPTY_LIST; } final char listDelimiter = getListDelimiter(indexVersionNumber); final List tokens = CoreStringUtil.split(values,String.valueOf(listDelimiter)); final List result = new ArrayList(tokens.size()); for (Iterator iter = tokens.iterator(); iter.hasNext();) { String token = (String)iter.next(); if (token != null) { result.add(token); } } return result; } public static char getListDelimiter(final int indexVersionNumber) { if (indexVersionNumber < DELIMITER_INDEX_VERSION) { return IndexConstants.RECORD_STRING.LIST_DELIMITER_OLD; } return IndexConstants.RECORD_STRING.LIST_DELIMITER; } public static boolean includeMaterializationFlag(final int indexVersionNumber) { if (indexVersionNumber < TABLE_MATERIALIZATION_INDEX_VERSION) { return false; } return true; } public static boolean includeMaterializedTables(final int indexVersionNumber) { if (indexVersionNumber < TABLE_MATERIALIZATION_INDEX_VERSION) { return false; } return true; } public static boolean includeColumnNativeType(final int indexVersionNumber) { if (indexVersionNumber < COLUMN_NATIVE_TYPE_INDEX_VERSION) { return false; } return true; } public static boolean includeColumnNullDistinctValues(final int indexVersionNumber) { if (indexVersionNumber < COLUMN_NULL_DISTINCT_INDEX_VERSION) { return false; } return true; } public static boolean includePrimitiveTypeIdValue(final int indexVersionNumber) { if (indexVersionNumber < PRIMITIVE_TYPE_ID_INDEX_VERSION) { return false; } return true; } public static boolean includeInputParameterFlag(final int indexVersionNumber) { if (indexVersionNumber < COLUMN_INPUT_PARAMETER_FLAG_INDEX_VERSION) { return false; } return true; } public static boolean includeAnnotationProperties(final int indexVersionNumber) { if (indexVersionNumber < ANNOTATION_TAGS_INDEX_VERSION) { return true; } return false; } public static boolean includeTransformationUUID(final int indexVersionNumber) { if (indexVersionNumber < TRANSFORMATION_UUID_INDEX_VERSION) { return false; } return true; } private static boolean includeProcedureUpdateCount(final int indexVersionNumber) { return (indexVersionNumber >= PROCEDURE_UPDATE_COUNT_VERSION); } private static boolean includeTableCardinalityOfMinusOne(final int indexVersionNumber) { return (indexVersionNumber >= TABLE_CARDINALITY_MINUS_ONE_VERSION); } public static int getCurrentIndexVersionNumber() { return CURRENT_INDEX_VERSION; } // ================================================================================== // P R I V A T E M E T H O D S // ================================================================================== /** * Set the "header" values on the specified MetadataRecord. * All index file record headers are of the form: * recordType|upperFullName|objectID|fullName|nameInSource|parentObjectID * The order of the fields in the index file header must also * be the order of the arguments in method signature. */ private static void setRecordHeaderValues(final AbstractMetadataRecord record, final String recordType, final String upperName, final String objectID, final String fullName, final String nameInSource, final String parentObjectID) { record.setRecordType(recordType.toCharArray()[0]); record.setUUID(getObjectValue(objectID)); record.setFullName(fullName); record.setNameInSource(getObjectValue(nameInSource)); record.setParentUUID(getObjectValue(parentObjectID)); } /** * Set the "footer" values on the specified MetadataRecord. * All index file record footers are of the form: * modelPath|name|indexVersion * The order of the fields in the index file header must also * be the order of the arguments in method signature. */ private static void setRecordFooterValues(final AbstractMetadataRecord record, final List tokens, int tokenIndex) { record.setResourcePath(getOptionalToken(tokens, tokenIndex++)); record.setName(getOptionalToken(tokens, tokenIndex++)); String version = getOptionalToken(tokens, tokenIndex++); if (version != null && version.length() > 0) { if (version.charAt(0) == IndexConstants.RECORD_STRING.INDEX_VERSION_MARKER) { version = version.substring(1); } try { record.setIndexVersion(Integer.parseInt(version)); } catch (NumberFormatException err) { // Log error } } } public static String getOptionalToken( final List tokens, int tokenIndex) { if(tokens.size() > tokenIndex) { return (String) tokens.get(tokenIndex); } return null; } }