package com.temenos.interaction.media.odata.xml.edmx; /* * #%L * interaction-media-odata-xml * %% * Copyright (C) 2012 - 2013 Temenos Holdings N.V. * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * #L% */ import java.io.Writer; import org.odata4j.core.NamespacedAnnotation; import org.odata4j.core.PrefixedNamespace; import org.odata4j.edm.EdmAnnotationAttribute; import org.odata4j.edm.EdmAnnotationElement; import org.odata4j.edm.EdmAssociation; import org.odata4j.edm.EdmAssociationSet; import org.odata4j.edm.EdmComplexType; import org.odata4j.edm.EdmDataServices; import org.odata4j.edm.EdmDocumentation; import org.odata4j.edm.EdmEntityContainer; import org.odata4j.edm.EdmEntitySet; import org.odata4j.edm.EdmEntityType; import org.odata4j.edm.EdmFunctionImport; import org.odata4j.edm.EdmFunctionParameter; import org.odata4j.edm.EdmItem; import org.odata4j.edm.EdmNavigationProperty; import org.odata4j.edm.EdmProperty; import org.odata4j.edm.EdmProperty.CollectionKind; import org.odata4j.edm.EdmSchema; import org.odata4j.format.xml.XmlFormatWriter; import org.odata4j.stax2.QName2; import org.odata4j.stax2.XMLFactoryProvider2; import org.odata4j.stax2.XMLWriter2; public class EdmxMetaDataWriter extends XmlFormatWriter { public static void write(EdmDataServices services, Writer w) { XMLWriter2 writer = XMLFactoryProvider2.getInstance() .newXMLWriterFactory2().createXMLWriter(w); writer.startDocument(); writer.startElement(new QName2(edmx, "Edmx", "edmx")); writer.writeAttribute("Version", "1.0"); writer.writeNamespace("edmx", edmx); writer.writeNamespace("d", d); writer.writeNamespace("m", m); writeExtensionNamespaces(services, writer); writer.startElement(new QName2(edmx, "DataServices", "edmx")); writer.writeAttribute(new QName2(m, "DataServiceVersion", "m"), "1.0"); // Schema for (EdmSchema schema : services.getSchemas()) { writer.startElement(new QName2("Schema"), edm); writer.writeAttribute("Namespace", schema.getNamespace()); writeAnnotationAttributes(schema, writer); writeDocumentation(schema, writer); // ComplexType for (EdmComplexType ect : schema.getComplexTypes()) { writer.startElement(new QName2("ComplexType")); writer.writeAttribute("Name", ect.getName()); if (null != ect.getIsAbstract()) { writer.writeAttribute("Abstract", ect.getIsAbstract() .toString()); } writeAnnotationAttributes(ect, writer); writeDocumentation(ect, writer); writeProperties(ect.getProperties(), writer); writeAnnotationElements(ect, writer); writer.endElement("ComplexType"); } // EntityType for (EdmEntityType eet : schema.getEntityTypes()) { writer.startElement(new QName2("EntityType")); String entityName = eet.getName(); writer.writeAttribute("Name", entityName); if (null != eet.getIsAbstract()) { writer.writeAttribute("Abstract", eet.getIsAbstract() .toString()); } if (Boolean.TRUE.equals(eet.getHasStream())) { writer.writeAttribute(new QName2(m, "HasStream", "m"), "true"); } // keys only on base types if (eet.isRootType()) { writeAnnotationAttributes(eet, writer); writeDocumentation(eet, writer); writer.startElement(new QName2("Key")); for (String key : eet.getKeys()) { writer.startElement(new QName2("PropertyRef")); writer.writeAttribute("Name", key); writer.endElement("PropertyRef"); } writer.endElement("Key"); } else { writer.writeAttribute("BaseType", eet.getBaseType() .getFullyQualifiedTypeName()); writeAnnotationAttributes(eet, writer); writeDocumentation(eet, writer); } writeProperties(eet.getDeclaredProperties(), writer); for (EdmNavigationProperty np : eet .getDeclaredNavigationProperties()) { writer.startElement(new QName2("NavigationProperty")); writer.writeAttribute("Name", np.getName()); writer.writeAttribute("Relationship", np.getRelationship() .getFQNamespaceName()); writer.writeAttribute("FromRole", np.getFromRole() .getRole()); writer.writeAttribute("ToRole", np.getToRole().getRole()); writeAnnotationAttributes(np, writer); writeDocumentation(np, writer); writeAnnotationElements(np, writer); writer.endElement("NavigationProperty"); } writeAnnotationElements(eet, writer); writer.endElement("EntityType"); } // Association for (EdmAssociation assoc : schema.getAssociations()) { writer.startElement(new QName2("Association")); writer.writeAttribute("Name", assoc.getName()); writeAnnotationAttributes(assoc, writer); writeDocumentation(assoc, writer); writer.startElement(new QName2("End")); writer.writeAttribute("Role", assoc.getEnd1().getRole()); writer.writeAttribute("Type", assoc.getEnd1().getType() .getFullyQualifiedTypeName()); writer.writeAttribute("Multiplicity", assoc.getEnd1() .getMultiplicity().getSymbolString()); writer.endElement("End"); writer.startElement(new QName2("End")); writer.writeAttribute("Role", assoc.getEnd2().getRole()); writer.writeAttribute("Type", assoc.getEnd2().getType() .getFullyQualifiedTypeName()); writer.writeAttribute("Multiplicity", assoc.getEnd2() .getMultiplicity().getSymbolString()); writer.endElement("End"); writeAnnotationElements(assoc, writer); writer.endElement("Association"); } // EntityContainer for (EdmEntityContainer container : schema.getEntityContainers()) { writer.startElement(new QName2("EntityContainer")); writer.writeAttribute("Name", container.getName()); writer.writeAttribute(new QName2(m, "IsDefaultEntityContainer", "m"), Boolean.toString(container.isDefault())); writeAnnotationAttributes(container, writer); writeDocumentation(container, writer); for (EdmEntitySet ees : container.getEntitySets()) { writer.startElement(new QName2("EntitySet")); writer.writeAttribute("Name", ees.getName()); writer.writeAttribute("EntityType", ees.getType() .getFullyQualifiedTypeName()); writeAnnotationAttributes(ees, writer); writeDocumentation(ees, writer); writeAnnotationElements(ees, writer); writer.endElement("EntitySet"); } for (EdmFunctionImport fi : container.getFunctionImports()) { writer.startElement(new QName2("FunctionImport")); writer.writeAttribute("Name", fi.getName()); if (null != fi.getEntitySet()) { writer.writeAttribute("EntitySet", fi.getEntitySet() .getName()); } if (fi.getReturnType() != null) { // TODO: how to differentiate inline ReturnType vs // embedded ReturnType? writer.writeAttribute("ReturnType", fi.getReturnType() .getFullyQualifiedTypeName()); } writer.writeAttribute(new QName2(m, "HttpMethod", "m"), fi.getHttpMethod()); writeAnnotationAttributes(fi, writer); writeDocumentation(fi, writer); for (EdmFunctionParameter param : fi.getParameters()) { writer.startElement(new QName2("Parameter")); writer.writeAttribute("Name", param.getName()); writer.writeAttribute("Type", param.getType() .getFullyQualifiedTypeName()); if (param.getMode() != null) writer.writeAttribute("Mode", param.getMode() .toString()); writeAnnotationAttributes(param, writer); writeDocumentation(param, writer); writeAnnotationElements(param, writer); writer.endElement("Parameter"); } writeAnnotationElements(fi, writer); writer.endElement("FunctionImport"); } for (EdmAssociationSet eas : container.getAssociationSets()) { writer.startElement(new QName2("AssociationSet")); writer.writeAttribute("Name", eas.getName()); writer.writeAttribute("Association", eas.getAssociation() .getFQNamespaceName()); writeAnnotationAttributes(eas, writer); writeDocumentation(eas, writer); writer.startElement(new QName2("End")); writer.writeAttribute("Role", eas.getEnd1().getRole() .getRole()); writer.writeAttribute("EntitySet", eas.getEnd1() .getEntitySet().getName()); writer.endElement("End"); writer.startElement(new QName2("End")); writer.writeAttribute("Role", eas.getEnd2().getRole() .getRole()); writer.writeAttribute("EntitySet", eas.getEnd2() .getEntitySet().getName()); writer.endElement("End"); writeAnnotationElements(eas, writer); writer.endElement("AssociationSet"); } writeAnnotationElements(container, writer); writer.endElement("EntityContainer"); } writeAnnotationElements(schema, writer); writer.endElement("Schema"); } writer.endDocument(); } /** * Extensions to CSDL like Annotations appear in an application specific set * of namespaces. */ private static void writeExtensionNamespaces(EdmDataServices services, XMLWriter2 writer) { if (null != services.getNamespaces()) { for (PrefixedNamespace ns : services.getNamespaces()) { writer.writeNamespace(ns.getPrefix(), ns.getUri()); } } } private static void writeProperties(Iterable<EdmProperty> properties, XMLWriter2 writer) { for (EdmProperty prop : properties) { writer.startElement(new QName2("Property")); writer.writeAttribute("Name", prop.getName()); writer.writeAttribute("Type", prop.getType() .getFullyQualifiedTypeName()); writer.writeAttribute("Nullable", prop.isNullable() ? "true" : "false"); if (prop.getMaxLength() != null) { writer.writeAttribute("MaxLength", Integer.toString(prop.getMaxLength())); } if (!prop.getCollectionKind().equals(CollectionKind.NONE)) { writer.writeAttribute("CollectionKind", prop .getCollectionKind().toString()); } if (prop.getDefaultValue() != null) { writer.writeAttribute("DefaultValue", prop.getDefaultValue()); } if (prop.getPrecision() != null) { writer.writeAttribute("Precision", Integer.toString(prop.getPrecision())); } if (prop.getScale() != null) { writer.writeAttribute("Scale", Integer.toString(prop.getPrecision())); } writeAnnotationAttributes(prop, writer); writeAnnotationElements(prop, writer); writer.endElement("Property"); } } private static void writeAnnotationAttributes(EdmItem item, XMLWriter2 writer) { if (null != item.getAnnotations()) { for (NamespacedAnnotation<?> a : item.getAnnotations()) { if (a instanceof EdmAnnotationAttribute) { writer.writeAttribute(new QName2(a.getNamespace().getUri(), a.getName(), a.getNamespace().getPrefix()), a .getValue() == null ? "" : a.getValue().toString()); } } } } private static void writeAnnotationElements(EdmItem item, XMLWriter2 writer) { if (null != item.getAnnotations()) { for (NamespacedAnnotation<?> a : item.getAnnotations()) { if (a instanceof EdmAnnotationElement) { // TODO: please don't throw an exception here. // this totally breaks ODataConsumer even thought it doesn't // rely // on annotations. A no-op is a interim approach that allows // work // to proceed by those using queryable metadata to access // annotations. // throw new // UnsupportedOperationException("Implement element annotations"); } } } } private static void writeDocumentation(EdmItem item, XMLWriter2 writer) { EdmDocumentation doc = item.getDocumentation(); if (null != doc && (null != doc.getSummary() || null != doc .getLongDescription())) { QName2 d = new QName2(edm, "Documentation"); writer.startElement(d); { if (null != doc.getSummary()) { QName2 s = new QName2(edm, "Summary"); writer.startElement(s); writer.writeText(doc.getSummary()); writer.endElement(s.getLocalPart()); } if (null != doc.getLongDescription()) { QName2 s = new QName2(edm, "LongDescription"); writer.startElement(s); writer.writeText(doc.getLongDescription()); writer.endElement(s.getLocalPart()); } } writer.endElement(d.getLocalPart()); } } }