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

      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";

        for (ProcedureParameter pp : proc.getParameters()) {
          if (pp.getName().equals("return")) {
            httpMethod = "GET";
            edmProcedure.setReturnType(
                ODataTypeManager.odataType(pp.getDatatype().getRuntimeTypeName()));
            continue;
          }

          EdmFunctionParameter.Builder param = EdmFunctionParameter.newBuilder();
          param.setName(pp.getName());
          param.setType(ODataTypeManager.odataType(pp.getDatatype().getRuntimeTypeName()));

          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";
          EdmComplexType.Builder complexType = EdmComplexType.newBuilder();
          complexType.setName(proc.getName() + "_" + returnColumns.getName());
          complexType.setNamespace(schema.getName());
          for (Column c : returnColumns.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);
            }
            complexType.addProperties(property);
          }
          odataSchema.addComplexTypes(complexType);
          edmProcedure.setIsCollection(true);
          edmProcedure.setReturnType(
              EdmCollectionType.newBuilder()
                  .setCollectionType(complexType)
                  .setKind(CollectionKind.Collection));
        }
        edmProcedure.setHttpMethod(httpMethod);
        entityContainer.addFunctionImports(edmProcedure);
      }
    }
  }
  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);
        }
      }
    }
  }