/* * JBoss, Home of Professional Open Source. * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. Some portions may be licensed * to Red Hat, Inc. under one or more contributor license agreements. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. */ package org.teiid.olingo.service; import static org.teiid.language.visitor.SQLStringVisitor.*; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.apache.olingo.commons.api.edm.EdmPrimitiveTypeKind; import org.apache.olingo.commons.api.edm.FullQualifiedName; import org.apache.olingo.commons.api.edm.provider.*; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlCollection; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlConstantExpression; import org.apache.olingo.commons.api.edm.provider.annotation.CsdlConstantExpression.ConstantExpressionType; import org.teiid.core.TeiidRuntimeException; import org.teiid.core.types.DataTypeManager; import org.teiid.logging.LogConstants; import org.teiid.logging.LogManager; import org.teiid.metadata.BaseColumn.NullType; import org.teiid.metadata.Column; import org.teiid.metadata.ColumnSet; import org.teiid.metadata.ForeignKey; import org.teiid.metadata.KeyRecord; import org.teiid.metadata.MetadataFactory; import org.teiid.metadata.Procedure; import org.teiid.metadata.ProcedureParameter; import org.teiid.metadata.Table; import org.teiid.olingo.ODataPlugin; import org.teiid.olingo.common.ODataTypeManager; public class ODataSchemaBuilder { public static CsdlSchema buildMetadata(String namespace, org.teiid.metadata.Schema teiidSchema) { try { CsdlSchema csdlSchema = new CsdlSchema(); String fullSchemaName = namespace+"."+teiidSchema.getName(); csdlSchema.setNamespace(fullSchemaName).setAlias(teiidSchema.getName()); buildEntityTypes(namespace, teiidSchema, csdlSchema); buildProcedures(teiidSchema, csdlSchema); return csdlSchema; } catch (Exception e) { throw new TeiidRuntimeException(e); } } static CsdlEntitySet findEntitySet(CsdlSchema edmSchema, String enitityName) { CsdlEntityContainer entityContainter = edmSchema.getEntityContainer(); for (CsdlEntitySet entitySet : entityContainter.getEntitySets()) { if (entitySet.getName().equalsIgnoreCase(enitityName)) { return entitySet; } } return null; } static CsdlSchema findSchema(Map<String, CsdlSchema> edmSchemas, String schemaName) { return edmSchemas.get(schemaName); } static CsdlEntityType findEntityType(Map<String, CsdlSchema> edmSchemas, String schemaName, String enitityName) { CsdlSchema schema = findSchema(edmSchemas, schemaName); if (schema != null) { for (CsdlEntityType type : schema.getEntityTypes()) { if (type.getName().equalsIgnoreCase(enitityName)) { return type; } } } return null; } static CsdlEntityContainer findEntityContainer(Map<String, CsdlSchema> edmSchemas, String schemaName) { CsdlSchema schema = edmSchemas.get(schemaName); return schema.getEntityContainer(); } static void buildEntityTypes(String namespace, org.teiid.metadata.Schema schema, CsdlSchema csdlSchema) { Map<String, CsdlEntitySet> entitySets = new LinkedHashMap<String, CsdlEntitySet>(); Map<String, CsdlEntityType> entityTypes = new LinkedHashMap<String, CsdlEntityType>(); String fullSchemaName = namespace+"."+schema.getName(); for (Table table : schema.getTables().values()) { // skip if the table does not have the PK or unique KeyRecord primaryKey = getIdentifier(table); if ( primaryKey == null) { LogManager.logDetail(LogConstants.CTX_ODATA, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16017,table.getFullName())); continue; } String entityTypeName = table.getName(); CsdlEntityType entityType = new CsdlEntityType().setName(entityTypeName); // adding properties List<CsdlProperty> properties = new ArrayList<CsdlProperty>(); for (Column c : table.getColumns()) { boolean nullable = c.getNullType() == NullType.Nullable; CsdlProperty property = buildProperty(c, isPartOfPrimaryKey(table, c.getName())?false:nullable); addColumnAnnotations(c, property, csdlSchema); properties.add(property); } entityType.setProperties(properties); if (hasStream(properties)) { entityType.setHasStream(true); } // set keys ArrayList<CsdlPropertyRef> keyProps = new ArrayList<CsdlPropertyRef>(); for (Column c : primaryKey.getColumns()) { keyProps.add(new CsdlPropertyRef().setName(c.getName())); } entityType.setKey(keyProps); addTableAnnotations(table, entityType, csdlSchema); // entity set one for one entity type CsdlEntitySet entitySet = new CsdlEntitySet() .setName(table.getName()) .setType(new FullQualifiedName(fullSchemaName, table.getName())) .setIncludeInServiceDocument(true); // add entity types for entity schema entityTypes.put(entityTypeName, entityType); entitySets.put(entityTypeName, entitySet); } buildNavigationProperties(schema, entityTypes, entitySets); // entity container is holder entity sets, association sets, function // imports CsdlEntityContainer entityContainer = new CsdlEntityContainer().setName( schema.getName()).setEntitySets( new ArrayList<CsdlEntitySet>(entitySets.values())); // build entity schema csdlSchema.setEntityTypes(new ArrayList<CsdlEntityType>(entityTypes.values())) .setEntityContainer(entityContainer); } private static boolean hasStream(List<CsdlProperty> properties) { for (CsdlProperty p : properties) { if (p.getType().equals(EdmPrimitiveTypeKind.Binary.getFullQualifiedName())) { return true; } } return false; } private static CsdlProperty buildProperty(Column c, boolean nullable) { String runtimeType = c.getRuntimeType(); CsdlProperty property = new CsdlProperty() .setName(c.getName()) .setType(ODataTypeManager.odataType(runtimeType).getFullQualifiedName()) .setNullable(nullable); if (DataTypeManager.isArrayType(runtimeType)) { property.setCollection(true); } runtimeType = ODataTypeManager.teiidType(property.getType(), false); if (runtimeType.equals(DataTypeManager.DefaultDataTypes.STRING) || runtimeType.equals(DataTypeManager.DefaultDataTypes.VARBINARY)) { //this will not be correct for a multi-dimensional or other unsupported type property.setMaxLength(c.getLength()); if (runtimeType.equals(DataTypeManager.DefaultDataTypes.STRING)) { property.setUnicode(true); } } else if (runtimeType.equals(DataTypeManager.DefaultDataTypes.BIG_DECIMAL) || runtimeType.equals(DataTypeManager.DefaultDataTypes.BIG_INTEGER)) { if (c.getScale() < 0) { property.setPrecision((int)Math.min(Integer.MAX_VALUE, (long)c.getPrecision() - c.getScale())); } else { property.setPrecision(c.getPrecision()); } property.setScale(Math.max(0, c.getScale())); } else if (runtimeType.equals(DataTypeManager.DefaultDataTypes.TIMESTAMP) || runtimeType.equals(DataTypeManager.DefaultDataTypes.TIME)) { property.setPrecision(c.getPrecision() == 0?new Integer(4):c.getPrecision()); } if (c.getDefaultValue() != null) { property.setDefaultValue(c.getDefaultValue()); } return property; } static boolean isPartOfPrimaryKey(Table table, String columnName) { KeyRecord pk = table.getPrimaryKey(); if (hasColumn(pk, columnName)) { return true; } for (KeyRecord key:table.getUniqueKeys()) { if (hasColumn(key, columnName)) { return true; } } return false; } static boolean isPrimaryKey(Table table, List<String> columnNames) { KeyRecord pk = table.getPrimaryKey(); boolean isPK = true; for (String columnName:columnNames) { if (!hasColumn(pk, columnName)) { isPK = false; break; } } if (!isPK) { for (KeyRecord key:table.getUniqueKeys()) { isPK = true; for (String columnName:columnNames) { if (!hasColumn(key, columnName)) { isPK = false; } } if (isPK) { break; } } } return isPK; } static boolean hasColumn(KeyRecord pk, String columnName) { if (pk != null) { for (Column column : pk.getColumns()) { if (getRecordName(column).equals(columnName)) { return true; } } } return false; } static KeyRecord getIdentifier(Table table) { if (table.getPrimaryKey() != null) { return table.getPrimaryKey(); } for (KeyRecord key:table.getUniqueKeys()) { return key; } return null; } private static void buildNavigationProperties(org.teiid.metadata.Schema schema, Map<String, CsdlEntityType> entityTypes, Map<String, CsdlEntitySet> entitySets) { for (Table table : schema.getTables().values()) { // skip if the table does not have the PK or unique if (getIdentifier(table) == null) { continue; } // build Associations for (ForeignKey fk : table.getForeignKeys()) { // check to see if fk is part of this table's pk, then it is 1 to 1 relation boolean fkPKSame = sameColumnSet(getIdentifier(table), fk); boolean fkIsPK= isPrimaryKey(schema.getTable(fk.getReferenceTableName()), fk.getReferenceColumns()); addForwardNavigation(entityTypes, entitySets, table, fk, fkPKSame || fkIsPK); addReverseNavigation(entityTypes, entitySets, table, fk, fkPKSame && fkIsPK); } } } private static void addForwardNavigation(Map<String, CsdlEntityType> entityTypes, Map<String, CsdlEntitySet> entitySets, Table table, ForeignKey fk, boolean onetoone) { CsdlNavigationProperty navigaton = null; CsdlNavigationPropertyBinding navigationBinding = null; String entityTypeName = null; entityTypeName = table.getName(); navigaton = buildNavigation(fk); navigationBinding = buildNavigationBinding(fk); if (onetoone ) { navigaton.setNullable(false); } else { navigaton.setCollection(true); } CsdlEntityType entityType = entityTypes.get(entityTypeName); entityType.getNavigationProperties().add(navigaton); CsdlEntitySet entitySet = entitySets.get(entityTypeName); entitySet.getNavigationPropertyBindings().add(navigationBinding); } private static void addReverseNavigation(Map<String, CsdlEntityType> entityTypes, Map<String, CsdlEntitySet> entitySets, Table table, ForeignKey fk, boolean onetoone) { CsdlNavigationProperty navigaton = null; CsdlNavigationPropertyBinding navigationBinding = null; String entityTypeName = null; entityTypeName = fk.getReferenceTableName(); navigaton = buildReverseNavigation(table, fk); navigationBinding = buildReverseNavigationBinding(table,fk); if (onetoone) { navigaton.setNullable(false); } else { navigaton.setCollection(true); } CsdlEntityType entityType = entityTypes.get(entityTypeName); entityType.getNavigationProperties().add(navigaton); CsdlEntitySet entitySet = entitySets.get(entityTypeName); entitySet.getNavigationPropertyBindings().add(navigationBinding); } private static CsdlNavigationPropertyBinding buildNavigationBinding(ForeignKey fk) { CsdlNavigationPropertyBinding navigationBinding = new CsdlNavigationPropertyBinding(); navigationBinding.setPath(fk.getName()); navigationBinding.setTarget(fk.getReferenceTableName()); return navigationBinding; } private static CsdlNavigationPropertyBinding buildReverseNavigationBinding(Table table, ForeignKey fk) { CsdlNavigationPropertyBinding navigationBinding = new CsdlNavigationPropertyBinding(); navigationBinding.setPath(table.getName()+"_"+fk.getName()); navigationBinding.setTarget(table.getName()); return navigationBinding; } private static CsdlNavigationProperty buildNavigation(ForeignKey fk) { String refSchemaName = fk.getReferenceKey().getParent().getParent().getName(); CsdlNavigationProperty navigaton = new CsdlNavigationProperty(); navigaton.setName(fk.getName()).setType(new FullQualifiedName(refSchemaName, fk.getReferenceTableName())); ArrayList<CsdlReferentialConstraint> constrainsts = new ArrayList<CsdlReferentialConstraint>(); for (int i = 0; i < fk.getColumns().size(); i++) { Column c = fk.getColumns().get(i); String refColumn = fk.getReferenceColumns().get(i); CsdlReferentialConstraint constraint = new CsdlReferentialConstraint(); constraint.setProperty(c.getName()); constraint.setReferencedProperty(refColumn); } navigaton.setReferentialConstraints(constrainsts); return navigaton; } private static CsdlNavigationProperty buildReverseNavigation(Table table, ForeignKey fk) { String refSchemaName = table.getParent().getName(); CsdlNavigationProperty navigaton = new CsdlNavigationProperty(); navigaton.setName(table.getName() + "_" + fk.getName()).setType( new FullQualifiedName(refSchemaName, table.getName())); ArrayList<CsdlReferentialConstraint> constrainsts = new ArrayList<CsdlReferentialConstraint>(); for (int i = 0; i < fk.getColumns().size(); i++) { Column c = fk.getColumns().get(i); String refColumn = fk.getReferenceColumns().get(i); CsdlReferentialConstraint constraint = new CsdlReferentialConstraint(); constraint.setProperty(refColumn); constraint.setReferencedProperty(c.getName()); } navigaton.setReferentialConstraints(constrainsts); return navigaton; } static void buildProcedures(org.teiid.metadata.Schema schema, CsdlSchema csdlSchema) { // procedures ArrayList<CsdlComplexType> complexTypes = new ArrayList<CsdlComplexType>(); ArrayList<CsdlFunction> functions = new ArrayList<CsdlFunction>(); ArrayList<CsdlFunctionImport> functionImports = new ArrayList<CsdlFunctionImport>(); ArrayList<CsdlAction> actions = new ArrayList<CsdlAction>(); ArrayList<CsdlActionImport> actionImports = new ArrayList<CsdlActionImport>(); for (Procedure proc : schema.getProcedures().values()) { if (!allowedProcedure(proc)){ LogManager.logDetail(LogConstants.CTX_ODATA, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16032, proc.getFullName())); continue; } if (isFuntion(proc)) { buildFunction(schema.getName(), proc, complexTypes, functions, functionImports, csdlSchema); } else { buildAction(schema.getName(), proc, complexTypes, actions, actionImports, csdlSchema); } } csdlSchema.setComplexTypes(complexTypes); csdlSchema.setFunctions(functions); csdlSchema.setActions(actions); csdlSchema.getEntityContainer().setFunctionImports(functionImports); csdlSchema.getEntityContainer().setActionImports(actionImports); } private static boolean doesProcedureReturn(Procedure proc) { for (ProcedureParameter pp : proc.getParameters()) { if (pp.getType().equals(ProcedureParameter.Type.ReturnValue)) { return true; } } return (proc.getResultSet() != null); } private static boolean allowedProcedure(Procedure proc) { // any number of in, but can have only one LOB if lob is present // only *one* result, or resultset allowed int inouts = 0; int lobs = 0; int outs = 0; for (ProcedureParameter pp : proc.getParameters()) { if (pp.getType().equals(ProcedureParameter.Type.Out)) { continue; } if (pp.getType().equals(ProcedureParameter.Type.In) || pp.getType().equals(ProcedureParameter.Type.InOut)) { inouts++; if (DataTypeManager.isLOB(pp.getRuntimeType())) { lobs++; } } else if (pp.getType().equals(ProcedureParameter.Type.ReturnValue)) { outs++; } } if (proc.getResultSet() != null) { for (Column c : proc.getResultSet().getColumns()) { if (DataTypeManager.isLOB(c.getRuntimeType())) { return false; } } outs++; } if (outs > 1) { return false; } if (inouts > 1 && lobs >= 1) { return false; } return true; } private static boolean isInputParameterLob(Procedure proc) { for (ProcedureParameter pp : proc.getParameters()) { if (!pp.getType().equals(ProcedureParameter.Type.ReturnValue) && DataTypeManager.isLOB(pp.getRuntimeType())) { return true; } } return false; } private static boolean isFuntion(Procedure proc) { if (doesProcedureReturn(proc) && proc.getUpdateCount() < 1 && !isInputParameterLob(proc)) { return true; } return false; } static void buildFunction(String schemaName, Procedure proc, ArrayList<CsdlComplexType> complexTypes, ArrayList<CsdlFunction> functions, ArrayList<CsdlFunctionImport> functionImports, CsdlSchema csdlSchema) { CsdlFunction edmFunction = new CsdlFunction(); edmFunction.setName(proc.getName()); edmFunction.setBound(false); ArrayList<CsdlParameter> params = new ArrayList<CsdlParameter>(); for (ProcedureParameter pp : proc.getParameters()) { EdmPrimitiveTypeKind odataType = ODataTypeManager.odataType(pp.getRuntimeType()); if (pp.getType().equals(ProcedureParameter.Type.ReturnValue)) { edmFunction.setReturnType(new CsdlReturnType().setType(odataType.getFullQualifiedName()).setCollection(DataTypeManager.isArrayType(pp.getRuntimeType()))); continue; } if (pp.getType().equals(ProcedureParameter.Type.In) || pp.getType().equals(ProcedureParameter.Type.InOut)) { CsdlParameter parameter = buildParameter(pp, odataType); addOperationParameterAnnotations(pp, parameter, csdlSchema); params.add(parameter); } } edmFunction.setParameters(params); // add a complex type for return resultset. ColumnSet<Procedure> returnColumns = proc.getResultSet(); if (returnColumns != null) { CsdlComplexType complexType = buildComplexType(proc, returnColumns, csdlSchema); complexTypes.add(complexType); FullQualifiedName odataType = new FullQualifiedName(schemaName, complexType.getName()); edmFunction.setReturnType((new CsdlReturnType().setType(odataType).setCollection(true))); } CsdlFunctionImport functionImport = new CsdlFunctionImport(); functionImport.setName(proc.getName()).setFunction(new FullQualifiedName(schemaName, proc.getName())); addOperationAnnotations(proc, edmFunction, csdlSchema); functions.add(edmFunction); functionImports.add(functionImport); } private static CsdlParameter buildParameter(ProcedureParameter pp, EdmPrimitiveTypeKind odatatype) { CsdlParameter param = new CsdlParameter(); param.setName(pp.getName()); param.setType(odatatype.getFullQualifiedName()); if (DataTypeManager.isArrayType(pp.getRuntimeType())) { param.setCollection(true); } param.setNullable(pp.getNullType() == NullType.Nullable); if (pp.getRuntimeType().equals(DataTypeManager.DefaultDataTypes.STRING)) { param.setMaxLength(pp.getLength()); } else if (pp.getRuntimeType().equals(DataTypeManager.DefaultDataTypes.BIG_DECIMAL)) { if (pp.getScale() < 0) { param.setPrecision((int)Math.min(Integer.MAX_VALUE, (long)pp.getPrecision() - pp.getScale())); } else { param.setPrecision(pp.getPrecision()); } param.setScale(Math.max(0, pp.getScale())); } else { if (pp.getDefaultValue() != null) { //param.setDefaultValue(pp.getDefaultValue()); } } return param; } static void buildAction(String schemaName, Procedure proc, ArrayList<CsdlComplexType> complexTypes, ArrayList<CsdlAction> actions, ArrayList<CsdlActionImport> actionImports, CsdlSchema csdlSchema) { CsdlAction edmAction = new CsdlAction(); edmAction.setName(proc.getName()); edmAction.setBound(false); ArrayList<CsdlParameter> params = new ArrayList<CsdlParameter>(); for (ProcedureParameter pp : proc.getParameters()) { EdmPrimitiveTypeKind odatatype = ODataTypeManager.odataType(pp.getRuntimeType()); if (pp.getType().equals(ProcedureParameter.Type.ReturnValue)) { edmAction.setReturnType(new CsdlReturnType().setType(odatatype.getFullQualifiedName()).setCollection(DataTypeManager.isArrayType(pp.getRuntimeType()))); continue; } if (pp.getType().equals(ProcedureParameter.Type.In) || pp.getType().equals(ProcedureParameter.Type.InOut)) { CsdlParameter parameter = buildParameter(pp, odatatype); addOperationParameterAnnotations(pp, parameter, csdlSchema); params.add(parameter); } } edmAction.setParameters(params); // add a complex type for return resultset. ColumnSet<Procedure> returnColumns = proc.getResultSet(); if (returnColumns != null) { CsdlComplexType complexType = buildComplexType(proc, returnColumns, csdlSchema); complexTypes.add(complexType); edmAction.setReturnType((new CsdlReturnType() .setType(new FullQualifiedName(schemaName, complexType .getName())).setCollection(true))); } CsdlActionImport actionImport = new CsdlActionImport(); actionImport.setName(proc.getName()).setAction(new FullQualifiedName(schemaName, proc.getName())); addOperationAnnotations(proc, edmAction, csdlSchema); actions.add(edmAction); actionImports.add(actionImport); } private static CsdlComplexType buildComplexType(Procedure proc, ColumnSet<Procedure> returnColumns, CsdlSchema csdlSchema) { CsdlComplexType complexType = new CsdlComplexType(); String entityTypeName = proc.getName() + "_" + returnColumns.getName(); //$NON-NLS-1$ complexType.setName(entityTypeName); ArrayList<CsdlProperty> props = new ArrayList<CsdlProperty>(); for (Column c : returnColumns.getColumns()) { CsdlProperty property = buildProperty(c, (c.getNullType() == NullType.Nullable)); props.add(property); addColumnAnnotations(c, property, csdlSchema); } complexType.setProperties(props); return complexType; } static List<String> getColumnNames(List<Column> columns) { ArrayList<String> names = new ArrayList<String>(); for (Column c : columns) { names.add(c.getName()); } return names; } static boolean sameColumnSet(KeyRecord recordOne, KeyRecord recordTwo) { if (recordOne == null || recordTwo == null) { return false; } List<Column> setOne = recordOne.getColumns(); List<Column> setTwo = recordTwo.getColumns(); if (setOne.size() != setTwo.size()) { return false; } for (int i = 0; i < setOne.size(); i++) { Column one = setOne.get(i); Column two = setTwo.get(i); if (!one.getName().equals(two.getName())) { return false; } } return true; } private static void addTableAnnotations(Table table, CsdlEntityType entityType, CsdlSchema csdlSchema) { if (table.getAnnotation() != null) { addStringAnnotation(entityType, "core.Description", table.getAnnotation()); } if (table.getCardinality() != -1) { addIntAnnotation(entityType, "teiid.CARDINALITY", table.getCardinality()); } if (table.isMaterialized()) { if (table.getMaterializedTable() != null) { addStringAnnotation(entityType, "teiid.MATERIALIZED_TABLE", table.getMaterializedTable().getFullName()); } if (table.getMaterializedStageTable() != null) { addStringAnnotation(entityType, "teiid.MATERIALIZED_STAGE_TABLE", table.getMaterializedStageTable().getFullName()); } } if (table.getNameInSource() != null) { addStringAnnotation(entityType, "teiid.NAMEINSOURCE", table.getNameInSource()); } if (table.getAccessPatterns() != null && !table.getAccessPatterns().isEmpty()) { ArrayList<String> values = new ArrayList<String>(); for (KeyRecord record:table.getAccessPatterns()) { StringBuilder sb = new StringBuilder(); for (Column c:record.getColumns()) { if (sb.length() > 0) { sb.append(","); } sb.append(c.getName()); } values.add(sb.toString()); } addStringCollectionAnnotation(entityType, "teiid.ACCESS_PATTERNS", values); } if (table.supportsUpdate()) { addBooleanAnnotation(entityType, "teiid.UPDATABLE", table.supportsUpdate()); } // add all custom properties for (String property:table.getProperties().keySet()) { addTerm(normalizeTermName(property), new String[] {"EntityType"}, csdlSchema); addStringAnnotation(entityType, csdlSchema.getAlias()+"."+normalizeTermName(property), table.getProperties().get(property)); } } private static void addColumnAnnotations(Column column, CsdlProperty property, CsdlSchema csdlSchema) { if (column.getAnnotation() != null) { addStringAnnotation(property, "core.Description", column.getAnnotation()); } if (column.getNameInSource() != null) { addStringAnnotation(property, "teiid.NAMEINSOURCE", column.getNameInSource()); } if (!column.isSelectable()) { addBooleanAnnotation(property, "teiid.SELECTABLE", column.isSelectable()); } if(column.isUpdatable()) { addBooleanAnnotation(property, "teiid.UPDATABLE", column.isUpdatable()); } if (column.isCurrency()) { addBooleanAnnotation(property, "teiid.CURRENCY", column.isCurrency()); } if (column.isCaseSensitive()) { addBooleanAnnotation(property, "teiid.CASE_SENSITIVE", column.isCaseSensitive()); } if (column.isFixedLength()) { addBooleanAnnotation(property, "teiid.FIXED_LENGTH", column.isFixedLength()); } if (!column.isSigned()) { addBooleanAnnotation(property, "teiid.SIGNED", column.isSigned()); } if (column.getDistinctValues() != -1) { addIntAnnotation(property, "teiid.DISTINCT_VALUES", column.getDistinctValues()); } if (column.getNullValues() != -1) { addIntAnnotation(property, "teiid.NULL_VALUE_COUNT", column.getNullValues()); } // add all custom properties for (String str:column.getProperties().keySet()) { addTerm(normalizeTermName(str), new String[] {"Property"}, csdlSchema); addStringAnnotation(property, csdlSchema.getAlias()+"."+normalizeTermName(str), column.getProperties().get(str)); } } private static void addOperationAnnotations(Procedure proc, CsdlOperation operation, CsdlSchema csdlSchema) { if (proc.getAnnotation() != null) { addStringAnnotation(operation, "core.Description", proc.getAnnotation()); } if (proc.getNameInSource() != null) { addStringAnnotation(operation, "teiid.NAMEINSOURCE", proc.getNameInSource()); } if (proc.getUpdateCount() != Procedure.AUTO_UPDATECOUNT) { addIntAnnotation(operation, "teiid.UPDATECOUNT", proc.getUpdateCount()); } // add all custom properties for (String str:proc.getProperties().keySet()) { addTerm(normalizeTermName(str), new String[] {"Action", "Function"}, csdlSchema); addStringAnnotation(operation, csdlSchema.getAlias()+"."+normalizeTermName(str), proc.getProperties().get(str)); } } private static void addOperationParameterAnnotations( ProcedureParameter procedure, CsdlParameter parameter, CsdlSchema csdlSchema) { if (procedure.getAnnotation() != null) { addStringAnnotation(parameter, "core.Description", procedure.getAnnotation()); } if (procedure.getNameInSource() != null) { addStringAnnotation(parameter, "teiid.NAMEINSOURCE", procedure.getNameInSource()); } // add all custom properties for (String str:procedure.getProperties().keySet()) { addTerm(normalizeTermName(str), new String[] {"Parameter"}, csdlSchema); addStringAnnotation(parameter, csdlSchema.getAlias()+"."+normalizeTermName(str), procedure.getProperties().get(str)); } } private static void addStringAnnotation(CsdlAnnotatable recipent, String term, String value) { CsdlAnnotation annotation = new CsdlAnnotation(); annotation.setTerm(term); annotation.setExpression(new CsdlConstantExpression(ConstantExpressionType.String, value)); recipent.getAnnotations().add(annotation); } private static void addIntAnnotation(CsdlAnnotatable recipent, String term, int value) { CsdlAnnotation annotation = new CsdlAnnotation(); annotation.setTerm(term); annotation.setExpression(new CsdlConstantExpression(ConstantExpressionType.Int, String.valueOf(value))); recipent.getAnnotations().add(annotation); } private static void addBooleanAnnotation(CsdlAnnotatable recipent, String term, boolean value) { CsdlAnnotation annotation = new CsdlAnnotation(); annotation.setTerm(term); annotation.setExpression(new CsdlConstantExpression(ConstantExpressionType.Bool, String.valueOf(value))); recipent.getAnnotations().add(annotation); } private static void addStringCollectionAnnotation(CsdlAnnotatable recipent, String term, List<String> values) { CsdlAnnotation annotation = new CsdlAnnotation(); annotation.setTerm(term); CsdlCollection collection = new CsdlCollection(); for (String value:values) { collection.getItems().add(new CsdlConstantExpression(ConstantExpressionType.String, value)); } annotation.setExpression(collection); recipent.getAnnotations().add(annotation); } private static void addTerm(String property, String[] appliesTo, CsdlSchema schema) { CsdlTerm term = schema.getTerm(property); if ( term == null) { term = new CsdlTerm(); term.setName(property); term.setType("Edm.String"); schema.getTerms().add(term); } if (term.getAppliesTo().isEmpty() || !term.getAppliesTo().contains(appliesTo)) { for (int i = 0; i < appliesTo.length; i++) { term.getAppliesTo().add(appliesTo[i]); } } } private static String normalizeTermName(String name) { if (name.startsWith("{")) { int end = name.indexOf("}"); if (end != -1) { String modified = null; String namespace = name.substring(1, end); for (Map.Entry<String, String> entry:MetadataFactory.BUILTIN_NAMESPACES.entrySet()) { if (entry.getValue().equals(namespace)) { modified = entry.getKey(); break; } } return modified + ":" + name.substring(end+1); } } return name; } }