/* * 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.translator.odata; import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import org.core4j.Enumerable; import org.odata4j.edm.*; import org.odata4j.edm.EdmFunctionParameter.Mode; import org.odata4j.edm.EdmProperty.CollectionKind; import org.odata4j.edm.EdmSchema.Builder; 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.*; public class ODataEntitySchemaBuilder { static EdmDataServices buildMetadata(MetadataStore metadataStore) { try { List<EdmSchema.Builder> edmSchemas = new ArrayList<EdmSchema.Builder>(); for (Schema schema:metadataStore.getSchemaList()) { buildEntityTypes(schema, edmSchemas, true); buildFunctionImports(schema, edmSchemas, true); buildAssosiationSets(schema, edmSchemas, true); } return EdmDataServices.newBuilder().addSchemas(edmSchemas).build(); } catch (Exception e) { throw new TeiidRuntimeException(e); } } static EdmDataServices buildMetadata(Schema schema) { try { List<EdmSchema.Builder> edmSchemas = new ArrayList<EdmSchema.Builder>(); buildEntityTypes(schema, edmSchemas, true); buildFunctionImports(schema, edmSchemas, true); buildAssosiationSets(schema, edmSchemas, true); return EdmDataServices.newBuilder().addSchemas(edmSchemas).build(); } catch (Exception e) { throw new TeiidRuntimeException(e); } } static org.odata4j.edm.EdmEntitySet.Builder findEntitySet(List<Builder> edmSchemas, String schemaName, String enitityName) { for (EdmSchema.Builder modelSchema:edmSchemas) { if (modelSchema.getNamespace().equals(schemaName)) { for (EdmEntityContainer.Builder entityContainter:modelSchema.getEntityContainers()) { for(EdmEntitySet.Builder entitySet:entityContainter.getEntitySets()) { if (entitySet.getName().equals(enitityName)) { return entitySet; } } } } } return null; } static EdmSchema.Builder findSchema(List<Builder> edmSchemas, String schemaName) { for (EdmSchema.Builder modelSchema:edmSchemas) { if (modelSchema.getNamespace().equals(schemaName)) { return modelSchema; } } return null; } static org.odata4j.edm.EdmEntityType.Builder findEntityType(List<Builder> edmSchemas, String schemaName, String enitityName) { for (EdmSchema.Builder modelSchema:edmSchemas) { if (modelSchema.getNamespace().equals(schemaName)) { for (EdmEntityType.Builder type:modelSchema.getEntityTypes()) { if (type.getName().equals(enitityName)) { return type; } } } } return null; } static EdmEntityContainer.Builder findEntityContainer(List<Builder> edmSchemas, String schemaName) { for (EdmSchema.Builder modelSchema:edmSchemas) { if (modelSchema.getNamespace().equals(schemaName)) { for (EdmEntityContainer.Builder entityContainter:modelSchema.getEntityContainers()) { return entityContainter; } } } return null; } public static void buildEntityTypes(Schema schema, List<EdmSchema.Builder> edmSchemas, boolean preserveEntityTypeName) { List<EdmEntitySet.Builder> entitySets = new ArrayList<EdmEntitySet.Builder>(); List<EdmEntityType.Builder> entityTypes = new ArrayList<EdmEntityType.Builder>(); LinkedHashMap<String, EdmComplexType.Builder> complexTypes = new LinkedHashMap<String, EdmComplexType.Builder>(); if (preserveEntityTypeName) { //first pass, build complex types for (Table table: schema.getTables().values()) { // skip if the table does not have the PK or unique KeyRecord primaryKey = table.getPrimaryKey(); List<KeyRecord> uniques = table.getUniqueKeys(); if (primaryKey == null && uniques.isEmpty()) { LogManager.logDetail(LogConstants.CTX_ODATA, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17017, table.getFullName())); continue; } for (Column c : table.getColumns()) { String columnGroup = c.getProperty(ODataMetadataProcessor.COLUMN_GROUP, false); String name = c.getName(); if (columnGroup != null) { //use the real name not the group_column name name = c.getSourceName(); } String complexType = c.getProperty(ODataMetadataProcessor.COMPLEX_TYPE, false); if (complexType == null) { continue; } EdmComplexType.Builder complexTypeBuilder = complexTypes.get(complexType); if (complexTypeBuilder == null) { complexTypeBuilder = EdmComplexType.newBuilder(); complexTypes.put(complexType, complexTypeBuilder); complexTypeBuilder.setName(complexType); complexTypeBuilder.setNamespace(schema.getName()); } else if (complexTypeBuilder.findProperty(name) != null) { continue; //already added } EdmProperty.Builder property = EdmProperty.newBuilder(name) .setType(ODataTypeManager.odataType(c.getRuntimeType())) .setNullable(isPartOfPrimaryKey(table, c.getName())?false:c.getNullType() == NullType.Nullable); if (c.getRuntimeType().equals(DataTypeManager.DefaultDataTypes.STRING)) { property.setFixedLength(c.isFixedLength()) .setMaxLength(c.getLength()) .setUnicode(true); } complexTypeBuilder.addProperties(property); } } } //second pass, add all columns for (Table table: schema.getTables().values()) { // skip if the table does not have the PK or unique KeyRecord primaryKey = table.getPrimaryKey(); List<KeyRecord> uniques = table.getUniqueKeys(); if (primaryKey == null && uniques.isEmpty()) { LogManager.logDetail(LogConstants.CTX_ODATA, ODataPlugin.Util.gs(ODataPlugin.Event.TEIID17017, table.getFullName())); continue; } String entityTypeName = table.getName(); if (preserveEntityTypeName && table.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false) != null) { entityTypeName = table.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false); } EdmEntityType.Builder entityType = EdmEntityType .newBuilder().setName(entityTypeName) .setNamespace(schema.getName()); // adding key if (primaryKey != null) { for (Column c : primaryKey.getColumns()) { entityType.addKeys(c.getName()); } } else { for (Column c : uniques.get(0).getColumns()) { entityType.addKeys(c.getName()); } } HashSet<String> columnGroups = new HashSet<String>(); // adding properties for (Column c : table.getColumns()) { String complexType = c.getProperty(ODataMetadataProcessor.COMPLEX_TYPE, false); if (complexType != null && preserveEntityTypeName) { String columnGroup = c.getProperty(ODataMetadataProcessor.COLUMN_GROUP, false); if (!columnGroups.add(columnGroup)) { continue; } EdmProperty.Builder property = EdmProperty.newBuilder(columnGroup) .setType(complexTypes.get(complexType).build()); entityType.addProperties(property); continue; } EdmProperty.Builder property = EdmProperty.newBuilder(c.getName()) .setType(ODataTypeManager.odataType(c.getRuntimeType())) .setNullable(isPartOfPrimaryKey(table, c.getName())?false:c.getNullType() == NullType.Nullable); if (c.getRuntimeType().equals(DataTypeManager.DefaultDataTypes.STRING)) { property.setFixedLength(c.isFixedLength()) .setMaxLength(c.getLength()) .setUnicode(true); } entityType.addProperties(property); } // entity set one for one entity type EdmEntitySet.Builder entitySet = EdmEntitySet.newBuilder() .setName(table.getName()) .setEntityType(entityType); entityType.setNamespace(schema.getName()); entitySets.add(entitySet); // add enitity types for entity schema entityTypes.add(entityType); } // entity container is holder entity sets, association sets, function imports EdmEntityContainer.Builder entityContainer = EdmEntityContainer .newBuilder().setName(schema.getName()) .setIsDefault(false) .addEntitySets(entitySets); // build entity schema EdmSchema.Builder modelSchema = EdmSchema.newBuilder() .setNamespace(schema.getName()) .addEntityTypes(entityTypes) .addEntityContainers(entityContainer) .addComplexTypes(complexTypes.values()); edmSchemas.add(modelSchema); } 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 hasColumn(KeyRecord pk, String columnName) { if (pk != null) { return pk.getColumnByName(columnName) != null; } return false; } private static String getEntityTypeName(Table t, boolean preserveEntityTypeName) { String entityTypeName = t.getName(); if (preserveEntityTypeName && t.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false) != null) { entityTypeName = t.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false); } return entityTypeName; } public static void buildAssosiationSets(Schema schema, List<Builder> edmSchemas, boolean preserveEntityTypeName) { EdmSchema.Builder odataSchema = findSchema(edmSchemas, schema.getName()); EdmEntityContainer.Builder entityContainer = findEntityContainer(edmSchemas, schema.getName()); List<EdmAssociationSet.Builder> assosiationSets = new ArrayList<EdmAssociationSet.Builder>(); List<EdmAssociation.Builder> assosiations = new ArrayList<EdmAssociation.Builder>(); for (Table table: schema.getTables().values()) { // skip if the table does not have the PK or unique KeyRecord primaryKey = table.getPrimaryKey(); List<KeyRecord> uniques = table.getUniqueKeys(); if (primaryKey == null && uniques.isEmpty()) { continue; } // build Associations for (ForeignKey fk:table.getForeignKeys()) { EdmEntitySet.Builder entitySet = findEntitySet(edmSchemas,schema.getName(), table.getName()); EdmEntitySet.Builder refEntitySet = findEntitySet(edmSchemas, schema.getName(),fk.getReferenceTableName()); EdmEntityType.Builder entityType = findEntityType(edmSchemas, schema.getName(), getEntityTypeName(table, preserveEntityTypeName)); EdmEntityType.Builder refEntityType = findEntityType(edmSchemas, schema.getName(), getEntityTypeName(fk.getPrimaryKey().getParent(), preserveEntityTypeName)); // check to see if fk is part of this table's pk, then it is 1 to 1 relation boolean onetoone = sameColumnSet(table.getPrimaryKey(), fk); // Build Association Ends EdmAssociationEnd.Builder endSelf = EdmAssociationEnd.newBuilder() .setRole(table.getName()) .setType(entityType) .setMultiplicity(onetoone?EdmMultiplicity.ZERO_TO_ONE:EdmMultiplicity.MANY); EdmAssociationEnd.Builder endRef = EdmAssociationEnd.newBuilder() .setRole(fk.getReferenceTableName()) .setType(refEntityType) .setMultiplicity(EdmMultiplicity.ZERO_TO_ONE); // Build Association EdmAssociation.Builder association = EdmAssociation.newBuilder(); association.setName(table.getName()+"_"+fk.getName()); //$NON-NLS-1$ association.setEnds(endSelf, endRef); association.setNamespace(refEntityType.getFullyQualifiedTypeName().substring(0, refEntityType.getFullyQualifiedTypeName().indexOf('.'))); assosiations.add(association); // Build ReferentialConstraint if (fk.getReferenceColumns() != null) { EdmReferentialConstraint.Builder erc = EdmReferentialConstraint.newBuilder(); erc.setPrincipalRole(fk.getReferenceTableName()); erc.addPrincipalReferences(fk.getReferenceColumns()); erc.setDependentRole(table.getName()); erc.addDependentReferences(getColumnNames(fk.getColumns())); association.setRefConstraint(erc); } if (!fk.getReferenceTableName().equalsIgnoreCase(table.getName())) { // Add EdmNavigationProperty to entity type String navigationName = getNavigationName(entityType, fk.getReferenceTableName()); EdmNavigationProperty.Builder nav = EdmNavigationProperty.newBuilder(navigationName); nav.setRelationshipName(fk.getName()); nav.setFromToName(table.getName(), fk.getReferenceTableName()); nav.setRelationship(association); nav.setFromTo(endSelf, endRef); entityType.addNavigationProperties(nav); } // Add EdmNavigationProperty to Reference entity type String navigationName = getNavigationName(refEntityType, table.getName()); EdmNavigationProperty.Builder refNav = EdmNavigationProperty.newBuilder(navigationName); refNav.setRelationshipName(fk.getName()); refNav.setFromToName(fk.getReferenceTableName(), table.getName()); refNav.setRelationship(association); refNav.setFromTo(endRef, endSelf); refEntityType.addNavigationProperties(refNav); // build AssosiationSet EdmAssociationSet.Builder assosiationSet = EdmAssociationSet.newBuilder() .setName(table.getName()+"_"+fk.getName()) //$NON-NLS-1$ .setAssociationName(fk.getName()); // Build AssosiationSet Ends EdmAssociationSetEnd.Builder endOne = EdmAssociationSetEnd.newBuilder() .setEntitySet(entitySet) .setRoleName(table.getName()) .setRole(EdmAssociationEnd.newBuilder().setType(entityType).setRole(entityType.getName())); EdmAssociationSetEnd.Builder endTwo = EdmAssociationSetEnd.newBuilder() .setEntitySet(refEntitySet) .setRoleName(fk.getReferenceTableName()) .setRole(EdmAssociationEnd.newBuilder().setType(refEntityType).setRole(refEntityType.getName())); assosiationSet.setEnds(endOne, endTwo); assosiationSet.setAssociation(association); assosiationSets.add(assosiationSet); } } entityContainer.addAssociationSets(assosiationSets); odataSchema.addAssociations(assosiations); } private static String getNavigationName( org.odata4j.edm.EdmEntityType.Builder entityType, String tableName) { String navigationName = tableName; int i = 1; while(true) { for (EdmNavigationProperty.Builder b:entityType.getNavigationProperties()) { if (b.getName().equals(navigationName)) { navigationName = null; break; } } if (navigationName != null) { return navigationName; } navigationName = tableName+(i++); } } public static void buildFunctionImports(Schema schema, List<Builder> edmSchemas, boolean preserveEntityTypeName) { EdmSchema.Builder odataSchema = findSchema(edmSchemas, schema.getName()); EdmEntityContainer.Builder entityContainer = findEntityContainer(edmSchemas, schema.getName()); // procedures for(Procedure proc:schema.getProcedures().values()) { EdmFunctionImport.Builder edmProcedure = EdmFunctionImport.newBuilder(); edmProcedure.setName(proc.getName()); String httpMethod = "POST"; //$NON-NLS-1$ for (ProcedureParameter pp:proc.getParameters()) { if (pp.getName().equals("return")) { //$NON-NLS-1$ httpMethod = "GET"; //$NON-NLS-1$ edmProcedure.setReturnType(ODataTypeManager.odataType(pp.getRuntimeType())); continue; } EdmFunctionParameter.Builder param = EdmFunctionParameter.newBuilder(); param.setName(pp.getName()); param.setType(ODataTypeManager.odataType(pp.getRuntimeType())); if (pp.getType() == ProcedureParameter.Type.In) { param.setMode(Mode.In); } else if (pp.getType() == ProcedureParameter.Type.InOut) { param.setMode(Mode.InOut); } else if (pp.getType() == ProcedureParameter.Type.Out) { param.setMode(Mode.Out); } param.setNullable(pp.getNullType() == NullType.Nullable); edmProcedure.addParameters(param); } // add a complex type for return resultset. ColumnSet<Procedure> returnColumns = proc.getResultSet(); if (returnColumns != null) { httpMethod = "GET"; //$NON-NLS-1$ EdmComplexType.Builder complexType = EdmComplexType.newBuilder(); String entityTypeName = proc.getName()+"_"+returnColumns.getName(); //$NON-NLS-1$ if (preserveEntityTypeName && proc.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false) != null) { entityTypeName = proc.getProperty(ODataMetadataProcessor.ENTITY_TYPE, false); } complexType.setName(entityTypeName); complexType.setNamespace(schema.getName()); for (Column c:returnColumns.getColumns()) { EdmProperty.Builder property = EdmProperty.newBuilder(c.getName()) .setType(ODataTypeManager.odataType(c.getRuntimeType())) .setNullable(c.getNullType() == NullType.Nullable); if (c.getRuntimeType().equals(DataTypeManager.DefaultDataTypes.STRING)) { property.setFixedLength(c.isFixedLength()) .setMaxLength(c.getLength()) .setUnicode(true); } complexType.addProperties(property); } odataSchema.addComplexTypes(complexType); edmProcedure.setIsCollection(true); edmProcedure.setReturnType(EdmCollectionType.newBuilder().setCollectionType(complexType).setKind(CollectionKind.Collection)); } edmProcedure.setHttpMethod(httpMethod); entityContainer.addFunctionImports(edmProcedure); } } 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; } static EdmEntitySet removeModelName(EdmEntitySet src) { EdmEntityType srcType = src.getType(); String schemaName = srcType.getName().substring(0, srcType.getName().indexOf('.')); String name = srcType.getName().substring(srcType.getName().indexOf('.')+1); EdmEntityType.Builder targetType = EdmEntityType .newBuilder().setName(name) .setNamespace(schemaName); targetType.addKeys(srcType.getKeys()); Enumerable<EdmProperty> properties = srcType.getProperties(); for (EdmProperty srcProperty:properties.toList()) { EdmProperty.Builder tgtProperty = EdmProperty.newBuilder(srcProperty.getName()) .setType(srcProperty.getType()) .setNullable(srcProperty.isNullable()) .setFixedLength(srcProperty.getFixedLength()) .setMaxLength(srcProperty.getMaxLength()) .setUnicode(true); targetType.addProperties(tgtProperty); } EdmEntitySet.Builder target = EdmEntitySet.newBuilder() .setName(src.getName()) .setEntityType(targetType); return target.build(); } }