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;
 }
  private static void buildAssosiationSets(MetadataStore metadataStore, List<Builder> edmSchemas) {
    for (Schema schema : metadataStore.getSchemaList()) {

      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()) {
        // 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(), table.getName());
          EdmEntityType.Builder refEntityType =
              findEntityType(edmSchemas, schema.getName(), fk.getReferenceTableName());

          // 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());
          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);
          }

          // Add EdmNavigationProperty to entity type
          EdmNavigationProperty.Builder nav =
              EdmNavigationProperty.newBuilder(fk.getReferenceTableName());
          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
          EdmNavigationProperty.Builder refNav = EdmNavigationProperty.newBuilder(table.getName());
          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())
                  .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);
    }
  }
  public static EdmEntitySet.Builder getBuilder(String namespace) {
    EdmEntityType.Builder modelBuilder = MdHierNodeWrapper.getEntityModel(namespace);

    EdmEntityType.Builder rowType =
        EdmEntityType.newBuilder().setNamespace(namespace).setName("Row");
    EdmEntityType.Builder mdDataDimType = MdDataDimWrapper.getEntityModel(namespace);
    EdmEntityType.Builder mdHierarchyType = MdHierarchyWrapper.getEntityModel(namespace);

    EdmAssociationEnd.Builder mdHierNodeRowEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("MdHierNode")
            .setType(modelBuilder)
            .setMultiplicity(EdmMultiplicity.ONE);
    EdmAssociationEnd.Builder rowMdHierNodeEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("Rows")
            .setType(rowType)
            .setMultiplicity(EdmMultiplicity.MANY);
    EdmAssociation.Builder mdHierNodeRow =
        EdmAssociation.newBuilder()
            .setName("MdHierNode-Rows")
            .setEnds(mdHierNodeRowEnd, rowMdHierNodeEnd);

    EdmAssociationEnd.Builder mdHierNodeMdDataDimEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("MdHierNode")
            .setType(modelBuilder)
            .setMultiplicity(EdmMultiplicity.ONE);
    EdmAssociationEnd.Builder mdDataDimMdHierNodeEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("MdDataDim")
            .setType(mdDataDimType)
            .setMultiplicity(EdmMultiplicity.MANY);
    EdmAssociation.Builder mdHierNodeMdDataDim =
        EdmAssociation.newBuilder()
            .setName("MdHierNode-MdDataDim")
            .setEnds(mdHierNodeMdDataDimEnd, mdDataDimMdHierNodeEnd);

    EdmAssociationEnd.Builder mdHierarchyMdHierNodeEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("MdHierarchy")
            .setType(mdHierarchyType)
            .setMultiplicity(EdmMultiplicity.ONE);
    EdmAssociationEnd.Builder mdHierNodeMdHierarchyEnd =
        EdmAssociationEnd.newBuilder()
            .setRole("MdHierNode")
            .setType(modelBuilder)
            .setMultiplicity(EdmMultiplicity.MANY);
    EdmAssociation.Builder mdHierNodeMdHierarchy =
        EdmAssociation.newBuilder()
            .setName("MdHierNode-MdHierarchy")
            .setEnds(mdHierNodeMdHierarchyEnd, mdHierarchyMdHierNodeEnd);

    EdmNavigationProperty.Builder nodeHierarchyNavigationProperty =
        EdmNavigationProperty.newBuilder("IdHierarchy")
            .setRelationship(mdHierNodeMdHierarchy)
            .setFromTo(mdHierNodeMdHierarchy.getEnd1(), mdHierNodeMdHierarchy.getEnd2());
    modelBuilder.addNavigationProperties(nodeHierarchyNavigationProperty);

    EdmNavigationProperty.Builder nodeRowsNavigationProperty =
        EdmNavigationProperty.newBuilder("NodeRows")
            .setRelationship(mdHierNodeRow)
            .setFromTo(mdHierNodeRow.getEnd1(), mdHierNodeRow.getEnd2());
    modelBuilder.addNavigationProperties(nodeRowsNavigationProperty);

    EdmNavigationProperty.Builder nodeDataNavigationProperty =
        EdmNavigationProperty.newBuilder("NodeData")
            .setRelationship(mdHierNodeMdDataDim)
            .setFromTo(mdHierNodeMdDataDim.getEnd1(), mdHierNodeMdDataDim.getEnd2());
    modelBuilder.addNavigationProperties(nodeDataNavigationProperty);

    return EdmEntitySet.newBuilder().setName("MdHierNode").setEntityType(modelBuilder);
  }
  private static void buildEntityTypes(
      MetadataStore metadataStore, List<EdmSchema.Builder> edmSchemas) {
    for (Schema schema : metadataStore.getSchemaList()) {

      List<EdmEntitySet.Builder> entitySets = new ArrayList<EdmEntitySet.Builder>();
      List<EdmEntityType.Builder> entityTypes = new ArrayList<EdmEntityType.Builder>();

      for (Table table : schema.getTables().values()) {

        KeyRecord primaryKey = table.getPrimaryKey();
        List<KeyRecord> uniques = table.getUniqueKeys();
        if (primaryKey == null && uniques.isEmpty()) {
          LogManager.logDetail(
              LogConstants.CTX_ODATA,
              ODataPlugin.Util.gs(ODataPlugin.Event.TEIID16002, table.getFullName()));
          continue;
        }

        EdmEntityType.Builder entityType =
            EdmEntityType.newBuilder().setName(table.getName()).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());
          }
        }

        // adding properties
        for (Column c : table.getColumns()) {
          EdmProperty.Builder property =
              EdmProperty.newBuilder(c.getName())
                  .setType(ODataTypeManager.odataType(c.getDatatype().getRuntimeTypeName()))
                  .setNullable(c.getNullType() == NullType.Nullable);
          if (c.getDatatype()
              .getRuntimeTypeName()
              .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);

      edmSchemas.add(modelSchema);
    }
  }
  private void resolve() {

    final Map<String, EdmEntityType.Builder> allEetsByFQName =
        Enumerable.create(dataServices.getEntityTypes())
            .toMap(EdmEntityType.Builder.func1_getFullyQualifiedTypeName());

    final Map<String, EdmEntityType.Builder> allEetsByFQAliasName =
        Enumerable.create(dataServices.getEntityTypes())
            .where(EdmEntityType.Builder.pred1_hasAlias())
            .toMap(EdmEntityType.Builder.func1_getFQAliasName());

    final Map<String, EdmAssociation.Builder> allEasByFQName =
        Enumerable.create(dataServices.getAssociations())
            .toMap(EdmAssociation.Builder.func1_getFQNamespaceName());

    for (EdmSchema.Builder edmSchema : dataServices.getSchemas()) {

      // resolve associations
      for (int i = 0; i < edmSchema.getAssociations().size(); i++) {
        EdmAssociation.Builder tmpAssociation = edmSchema.getAssociations().get(i);

        tmpAssociation
            .getEnd1()
            .setType(allEetsByFQName.get(tmpAssociation.getEnd1().getTypeName()));
        tmpAssociation
            .getEnd2()
            .setType(allEetsByFQName.get(tmpAssociation.getEnd2().getTypeName()));
      }

      // resolve navproperties
      for (EdmEntityType.Builder eet : edmSchema.getEntityTypes()) {
        List<EdmNavigationProperty.Builder> navProps = eet.getNavigationProperties();
        for (int i = 0; i < navProps.size(); i++) {
          final EdmNavigationProperty.Builder tmp = navProps.get(i);
          final EdmAssociation.Builder ea = allEasByFQName.get(tmp.getRelationshipName());
          if (ea == null)
            throw new IllegalArgumentException(
                "Invalid relationship name " + tmp.getRelationshipName());

          List<EdmAssociationEnd.Builder> finalEnds =
              Enumerable.create(tmp.getFromRoleName(), tmp.getToRoleName())
                  .select(
                      new Func1<String, EdmAssociationEnd.Builder>() {
                        public EdmAssociationEnd.Builder apply(String input) {
                          if (ea.getEnd1().getRole().equals(input)) return ea.getEnd1();
                          if (ea.getEnd2().getRole().equals(input)) return ea.getEnd2();
                          throw new IllegalArgumentException("Invalid role name " + input);
                        }
                      })
                  .toList();

          tmp.setRelationship(ea).setFromTo(finalEnds.get(0), finalEnds.get(1));
        }
      }

      // resolve entitysets
      for (EdmEntityContainer.Builder edmEntityContainer : edmSchema.getEntityContainers()) {
        for (int i = 0; i < edmEntityContainer.getEntitySets().size(); i++) {
          final EdmEntitySet.Builder tmpEes = edmEntityContainer.getEntitySets().get(i);
          EdmEntityType.Builder eet = allEetsByFQName.get(tmpEes.getEntityTypeName());
          if (eet == null) eet = allEetsByFQAliasName.get(tmpEes.getEntityTypeName());
          if (eet == null)
            throw new IllegalArgumentException("Invalid entity type " + tmpEes.getEntityTypeName());
          edmEntityContainer
              .getEntitySets()
              .set(
                  i,
                  EdmEntitySet.newBuilder()
                      .setName(tmpEes.getName())
                      .setEntityType(eet)
                      .setAnnotationElements(tmpEes.getAnnotationElements())
                      .setAnnotations(tmpEes.getAnnotations()));
        }
      }

      // resolve associationsets
      for (final EdmEntityContainer.Builder edmEntityContainer : edmSchema.getEntityContainers()) {
        for (int i = 0; i < edmEntityContainer.getAssociationSets().size(); i++) {
          final EdmAssociationSet.Builder tmpEas = edmEntityContainer.getAssociationSets().get(i);
          final EdmAssociation.Builder ea = allEasByFQName.get(tmpEas.getAssociationName());

          List<EdmAssociationSetEnd.Builder> finalEnds =
              Enumerable.create(tmpEas.getEnd1(), tmpEas.getEnd2())
                  .select(
                      new Func1<EdmAssociationSetEnd.Builder, EdmAssociationSetEnd.Builder>() {
                        public EdmAssociationSetEnd.Builder apply(
                            final EdmAssociationSetEnd.Builder input) {

                          EdmAssociationEnd.Builder eae =
                              ea.getEnd1().getRole().equals(input.getRoleName())
                                  ? ea.getEnd1()
                                  : ea.getEnd2().getRole().equals(input.getRoleName())
                                      ? ea.getEnd2()
                                      : null;

                          if (eae == null)
                            throw new IllegalArgumentException(
                                "Invalid role name " + input.getRoleName());

                          EdmEntitySet.Builder ees =
                              Enumerable.create(edmEntityContainer.getEntitySets())
                                  .first(
                                      OPredicates.nameEquals(
                                          EdmEntitySet.Builder.class, input.getEntitySetName()));
                          return EdmAssociationSetEnd.newBuilder()
                              .setRole(eae)
                              .setEntitySet(ees)
                              .setAnnotationElements(input.getAnnotationElements())
                              .setAnnotations(input.getAnnotations());
                        }
                      })
                  .toList();

          tmpEas.setAssociation(ea).setEnds(finalEnds.get(0), finalEnds.get(1));
        }
      }

      // resolve functionimports
      for (final EdmEntityContainer.Builder edmEntityContainer : edmSchema.getEntityContainers()) {
        for (int i = 0; i < edmEntityContainer.getFunctionImports().size(); i++) {
          final EdmFunctionImport.Builder tmpEfi = edmEntityContainer.getFunctionImports().get(i);
          EdmEntitySet.Builder ees =
              Enumerable.create(edmEntityContainer.getEntitySets())
                  .firstOrNull(
                      new Predicate1<EdmEntitySet.Builder>() {
                        public boolean apply(EdmEntitySet.Builder input) {
                          return input.getName().equals(tmpEfi.getEntitySetName());
                        }
                      });

          EdmType.Builder<?, ?> typeBuilder = null;
          if (tmpEfi.getReturnTypeName() != null) {
            typeBuilder = dataServices.resolveType(tmpEfi.getReturnTypeName());
            if (typeBuilder == null)
              throw new RuntimeException("Edm-type not found: " + tmpEfi.getReturnTypeName());

            if (tmpEfi.isCollection()) {
              typeBuilder =
                  EdmCollectionType.newBuilder()
                      .setKind(CollectionKind.Collection)
                      .setCollectionType(typeBuilder);
            }
          }

          edmEntityContainer
              .getFunctionImports()
              .set(
                  i,
                  EdmFunctionImport.newBuilder()
                      .setName(tmpEfi.getName())
                      .setEntitySet(ees)
                      .setReturnType(typeBuilder)
                      .setHttpMethod(tmpEfi.getHttpMethod())
                      .addParameters(tmpEfi.getParameters())
                      .setAnnotationElements(tmpEfi.getAnnotationElements())
                      .setAnnotations(tmpEfi.getAnnotations()));
        }
      }

      // resolve type hierarchy
      for (Entry<String, EdmEntityType.Builder> entry : allEetsByFQName.entrySet()) {
        String baseTypeName = entry.getValue().getFQBaseTypeName();
        if (baseTypeName != null) {
          EdmEntityType.Builder baseType = allEetsByFQName.get(baseTypeName);
          if (baseType == null) {
            throw new IllegalArgumentException("Invalid baseType: " + baseTypeName);
          }
          entry.getValue().setBaseType(baseType);
        }
      }
    }
  }