| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.sdo.helper.delegates; |
| |
| import commonj.sdo.Property; |
| import commonj.sdo.Type; |
| import commonj.sdo.helper.HelperContext; |
| |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.io.StringWriter; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import javax.xml.namespace.QName; |
| import javax.xml.transform.Source; |
| import javax.xml.transform.stream.StreamSource; |
| import org.eclipse.persistence.sdo.SDOConstants; |
| import org.eclipse.persistence.sdo.SDOProperty; |
| import org.eclipse.persistence.sdo.SDOType; |
| import org.eclipse.persistence.sdo.helper.DefaultSchemaResolver; |
| import org.eclipse.persistence.sdo.helper.SDOSchemaGenerator; |
| import org.eclipse.persistence.sdo.helper.SDOTypesGenerator; |
| import org.eclipse.persistence.sdo.helper.SDOXSDHelper; |
| import org.eclipse.persistence.sdo.helper.SchemaLocationResolver; |
| import org.eclipse.persistence.sdo.helper.SchemaResolver; |
| import org.eclipse.persistence.exceptions.SDOException; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.oxm.NamespaceResolver; |
| import org.eclipse.persistence.oxm.XMLField; |
| import org.eclipse.persistence.oxm.record.FormattedWriterRecord; |
| import org.eclipse.persistence.oxm.record.WriterRecord; |
| import org.w3c.dom.Element; |
| |
| /** |
| * <p><b>Purpose</b>: Provides access to additional information when the Type or Property is defined by an XML Schema (XSD).. |
| * <p><b>Responsibilities</b>:<ul> |
| * <li> Define methods defines Types from an XSD. |
| * <li> Generate methods an XSD from Types. |
| * <li> Other Methods return null/false otherwise or if the information is unavailable. |
| * </ul> |
| */ |
| public class SDOXSDHelperDelegate implements SDOXSDHelper { |
| |
| /*Map of global attributes keyed on qname*/ |
| private Map globalAttributes; |
| |
| /*Map of global elements keyed on qname*/ |
| private Map globalElements; |
| |
| // hold the context containing all helpers so that we can preserve inter-helper relationships |
| private HelperContext aHelperContext; |
| |
| public SDOXSDHelperDelegate(HelperContext aContext) { |
| aHelperContext = aContext; |
| globalAttributes = new HashMap(); |
| initOpenProps(); |
| globalElements = new HashMap(); |
| } |
| |
| /** |
| * Returns the local name as declared in the XSD. |
| * @param type to return local name for. |
| * @return the local name as declared in the XSD. |
| */ |
| @Override |
| public String getLocalName(Type type) { |
| if (type == null) { |
| return null; |
| } |
| return ((SDOType)type).getXsdLocalName(); |
| } |
| |
| /** |
| * Returns the local name as declared in the XSD. |
| * @param property to return local name for. |
| * @return the local name as declared in the XSD. |
| */ |
| @Override |
| public String getLocalName(Property property) { |
| if (property == null) { |
| return null; |
| } |
| return ((SDOProperty)property).getXsdLocalName(); |
| } |
| |
| /** |
| * Returns the namespace URI as declared in the XSD. |
| * @param type to return namespace URI for. |
| * @return the namespace URI as declared in the XSD. |
| */ |
| @Override |
| public String getNamespaceURI(Type type) { |
| if (type == null) { |
| return null; |
| } |
| return type.getURI(); |
| } |
| |
| /** |
| * Returns the namespace URI as declared in the XSD. |
| * @param property to return namespace URI for. |
| * @return the namespace URI as declared in the XSD. |
| */ |
| @Override |
| public String getNamespaceURI(Property property) { |
| if (property == null) { |
| return null; |
| } |
| String uri = null; |
| if (property.isOpenContent()) { |
| uri = ((SDOProperty)property).getUri(); |
| } else { |
| DatabaseMapping mapping = ((SDOProperty)property).getXmlMapping(); |
| if (mapping != null) { |
| XMLField xmlField = (XMLField) mapping.getField(); |
| if (xmlField != null && xmlField.getXPathFragment() != null) { |
| uri = xmlField.getXPathFragment().getNamespaceURI(); |
| } |
| } |
| } |
| return uri == null ? "" : uri; |
| } |
| |
| /** |
| * Returns true if the property is declared as an attribute in the XSD. |
| * Returns false if not known or for advanced cases. |
| * It is possible for both isAttribute and isElement to return false |
| * but they will not both return true. |
| * @param property to identify if an attribute. |
| * @return true if the property is declared as an attribute in the XSD. |
| */ |
| @Override |
| public boolean isAttribute(Property property) { |
| if (property == null) { |
| return false; |
| } |
| SDOProperty sdoProperty = (SDOProperty) property; |
| Object value = sdoProperty.get(SDOConstants.XMLELEMENT_PROPERTY); |
| if ((value != null) && value instanceof Boolean) { |
| boolean isElement = (Boolean) value; |
| if (isElement) { |
| return false; |
| } |
| } |
| |
| if ((sdoProperty.getOpposite() != null) && (sdoProperty.getOpposite().isContainment())) { |
| return false; |
| } else if (sdoProperty.isMany() || sdoProperty.isContainment() || sdoProperty.isNullable()) { |
| return false; |
| } |
| |
| // Case: open content non-attribute property |
| return true; |
| } |
| |
| /** |
| * Returns true if the property is declared as an element in the XSD. |
| * Returns false if not known or for advanced cases. |
| * It is possible for both isAttribute and isElement to return false |
| * but they will not both return true. |
| * @param property to identify if an element. |
| * @return true if the property is declared as an element in the XSD. |
| */ |
| @Override |
| public boolean isElement(Property property) { |
| if (property == null) { |
| return false; |
| } |
| SDOProperty sdoProperty = (SDOProperty) property; |
| Object value = sdoProperty.get(SDOConstants.XMLELEMENT_PROPERTY); |
| if ((value != null) && value instanceof Boolean) { |
| return (Boolean) value; |
| } |
| |
| if ((sdoProperty.getOpposite() != null) && (sdoProperty.getOpposite().isContainment())) { |
| return false; |
| } else if (sdoProperty.isMany() || sdoProperty.isContainment() || sdoProperty.isNullable()) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Returns true if the Type is declared to contain mixed content. |
| * A DataObject's mixed content values are typically accessed via a Sequence. |
| * @param type to identify if mixed content. |
| * @return true if the Type is declared to contain mixed content. |
| */ |
| @Override |
| public boolean isMixed(Type type) { |
| if (type == null) { |
| return false; |
| } |
| return type.isSequenced(); |
| } |
| |
| /** |
| * Indicates if this helper contains XSD information for the specified type. |
| * @param type the type. |
| * @return true if this helper contains XSD information for the specified type. |
| */ |
| @Override |
| public boolean isXSD(Type type) { |
| if (type == null) { |
| return false; |
| } |
| return ((SDOType)type).isXsd(); |
| } |
| |
| /** |
| * Returns the Property defined by the named global element or attribute |
| * in the targetNamespace uri, or null if not found. |
| * @param uri The uri of the targetNamespace. |
| * @param propertyName The name of the global property. |
| * @param isElement is true for global elements, false for global attributes. |
| * @return the Property defined by the named global element or attribute |
| * in the targetNamespace uri, or null if not found. |
| */ |
| @Override |
| public Property getGlobalProperty(String uri, String propertyName, boolean isElement) { |
| QName qname = new QName(uri, propertyName); |
| return getGlobalProperty(qname, isElement); |
| } |
| |
| @Override |
| public Property getGlobalProperty(QName qname, boolean isElement) { |
| if (isElement) { |
| return (Property)getGlobalElements().get(qname); |
| } else { |
| return (Property)getGlobalAttributes().get(qname); |
| } |
| } |
| |
| /** |
| * Return the appinfo declared for this Type and source. |
| * The appinfo start and end tags and content are returned. |
| * The xml namespace context is preserved in the appinfo element. |
| * If more than one appinfo with the same source is declared on the same |
| * Type their contents are concatenated. |
| * @param type the type with the appinfo declaration |
| * @param source the source of the appinfo declaration. |
| * @return the appinfo declared for this Type and source. |
| */ |
| @Override |
| public String getAppinfo(Type type, String source) { |
| if (type == null) { |
| throw SDOException.noAppInfoForNull(); |
| } |
| if (source == null) { |
| source = ""; |
| } |
| return (String)((SDOType)type).getAppInfoMap().get(source); |
| } |
| |
| /** |
| * Return the content of the appinfo declared for this Property and source. |
| * If the property is defined by ref= the appinfo of the referenced |
| * element or attribute is included. |
| * The appinfo start and end tags and content are returned. |
| * The xml namespace context is preserved in the appinfo element. |
| * If more than one appinfo with the same source is declared on the same |
| * Type their contents are concatenated. |
| * @param property the Property with the appinfo declaration |
| * @param source the source of the appinfo declaration. |
| * @return the appinfo declared for this Property and source. |
| */ |
| @Override |
| public String getAppinfo(Property property, String source) { |
| if (property == null) { |
| throw SDOException.noAppInfoForNull(); |
| } |
| if (source == null) { |
| source = ""; |
| } |
| return (String)((SDOProperty)property).getAppInfoMap().get(source); |
| } |
| |
| /** |
| * Define the XML Schema as Types. |
| * The Types are available through TypeHelper and DataGraph getType() methods. |
| * Same as define(new StringReader(xsd), null) |
| * @param xsd the XML Schema. |
| * @return the defined Types. |
| * @throws IllegalArgumentException if the Types could not be defined. |
| */ |
| @Override |
| public synchronized List define(String xsd) { |
| StringReader reader = new StringReader(xsd); |
| SchemaResolver schemaResolver = new DefaultSchemaResolver(); |
| return define(reader, schemaResolver); |
| } |
| |
| /** |
| * Define XML Schema as Types. |
| * The Types are available through TypeHelper and DataGraph getType() methods. |
| * @param xsdReader reader to an XML Schema. |
| * @param schemaLocation the URI of the location of the schema, used |
| * for processing relative imports and includes. May be null if not used. |
| * @return the defined Types. |
| * @throws IllegalArgumentException if the Types could not be defined. |
| */ |
| @Override |
| public synchronized List define(Reader xsdReader, String schemaLocation) { |
| DefaultSchemaResolver schemaResolver = new DefaultSchemaResolver(); |
| schemaResolver.setBaseSchemaLocation(schemaLocation); |
| |
| return define(new StreamSource(xsdReader), schemaResolver); |
| } |
| |
| /** |
| * Define XML Schema as Types. |
| * The Types are available through TypeHelper and DataGraph getType() methods. |
| * @param xsdReader reader to an XML Schema. |
| * @param schemaResolver the URI of the location of the schema, used |
| * for processing relative imports and includes. May be null if not used. |
| * @return the defined Types. |
| * @throws IllegalArgumentException if the Types could not be defined. |
| */ |
| public synchronized List define(Reader xsdReader, SchemaResolver schemaResolver) { |
| return define(new StreamSource(xsdReader), schemaResolver); |
| } |
| |
| @Override |
| public synchronized List define(Source xsdSource, SchemaResolver schemaResolver) { |
| return new SDOTypesGenerator(aHelperContext).define(xsdSource, schemaResolver); |
| } |
| |
| /** |
| * Define XML Schema as Types. |
| * The Types are available through TypeHelper and DataGraph getType() methods. |
| * @param xsdInputStream input stream to an XML Schema. |
| * @param schemaLocation the URI of the location of the schema, used |
| * for processing relative imports and includes. May be null if not used. |
| * @return the defined Types. |
| * @throws IllegalArgumentException if the Types could not be defined. |
| */ |
| @Override |
| public synchronized List define(InputStream xsdInputStream, String schemaLocation) { |
| InputStreamReader xsdReader = new InputStreamReader(xsdInputStream); |
| return define(xsdReader, schemaLocation); |
| } |
| |
| /** |
| * Generate an XML Schema Declaration (XSD) from Types. |
| * Same as generate(types, null); |
| * @param types a List containing the Types |
| * @return a String containing the generated XSD. |
| * @throws IllegalArgumentException if the XSD could not be generated. |
| */ |
| @Override |
| public String generate(List types) { |
| HashMap map = null; |
| return generate(types, map); |
| } |
| |
| /** |
| * Generate an XML Schema Declaration (XSD) from Types. |
| * Round trip from SDO to XSD to SDO is supported. |
| * Round trip from XSD to SDO to XSD is not supported. |
| * Use the original schema if one exists instead of generating a new one, as |
| * the generated XSD validates a different set of documents than the original XSD. |
| * Generating an XSD does not affect the XSDHelper or the Types. |
| * The Types must all have the same URI. |
| * The result is a String containing the generated XSD. |
| * All Types referenced with the same URI will be generated in the XSD |
| * and the list will be expanded to include all types generated. |
| * Any Types referenced with other URIs will cause |
| * imports to be produced as appropriate. |
| * Imports will include a schemaLocation if a Map is provided with an entry |
| * of the form key=import target namespace, value=schemaLocation |
| * @param types a List containing the Types |
| * @param namespaceToSchemaLocation map of target namespace to schema locations or null |
| * @return a String containing the generated XSD. |
| * @throws IllegalArgumentException if the XSD could not be generated. |
| */ |
| @Override |
| public String generate(List types, Map namespaceToSchemaLocation) { |
| return new SDOSchemaGenerator(aHelperContext).generate(types, namespaceToSchemaLocation); |
| } |
| |
| @Override |
| public String generate(List types, SchemaLocationResolver schemaLocationResolver) { |
| return new SDOSchemaGenerator(aHelperContext).generate(types, schemaLocationResolver); |
| } |
| |
| /** |
| * Assign a map of properties representing global attributes keyed on QName |
| * @param globalAttributes a Map of global elements keyed on QName |
| */ |
| public void setGlobalAttributes(Map globalAttributes) { |
| this.globalAttributes = globalAttributes; |
| } |
| |
| /** |
| * Return a map of properties representing global attributes keyed on QName |
| * @return a map of global attributes keyed on QName |
| */ |
| private Map getGlobalAttributes() { |
| return globalAttributes; |
| } |
| |
| private void initOpenProps() { |
| getGlobalAttributes().put(SDOConstants.MIME_TYPE_QNAME, SDOConstants.MIME_TYPE_PROPERTY); |
| getGlobalAttributes().put(SDOConstants.MIME_TYPE_PROPERTY_QNAME, SDOConstants.MIME_TYPE_PROPERTY_PROPERTY); |
| |
| Property xmlSchemaTypeProperty = aHelperContext.getTypeHelper().getOpenContentProperty(SDOConstants.SDO_URL, SDOConstants.XML_SCHEMA_TYPE_NAME); |
| getGlobalAttributes().put(SDOConstants.SCHEMA_TYPE_QNAME, xmlSchemaTypeProperty); |
| |
| getGlobalAttributes().put(SDOConstants.JAVA_CLASS_QNAME, SDOConstants.JAVA_CLASS_PROPERTY); |
| getGlobalAttributes().put(SDOConstants.XML_ELEMENT_QNAME, SDOConstants.XMLELEMENT_PROPERTY); |
| |
| Property xmlDataTypeProperty = aHelperContext.getTypeHelper().getOpenContentProperty(SDOConstants.SDOXML_URL, SDOConstants.SDOXML_DATATYPE); |
| getGlobalAttributes().put(SDOConstants.XML_DATATYPE_QNAME, xmlDataTypeProperty); |
| |
| getGlobalAttributes().put(SDOConstants.XML_ID_PROPERTY_QNAME, SDOConstants.ID_PROPERTY); |
| getGlobalAttributes().put(SDOConstants.DOCUMENTATION_PROPERTY_QNAME, SDOConstants.DOCUMENTATION_PROPERTY); |
| |
| getGlobalAttributes().put(SDOConstants.APPINFO_PROPERTY_QNAME, SDOConstants.APPINFO_PROPERTY); |
| } |
| |
| /** |
| * Assign a map of properties representing global elements keyed on QName |
| * @param globalElements a Map of global elements keyed on QName |
| */ |
| public void setGlobalElements(Map globalElements) { |
| this.globalElements = globalElements; |
| } |
| |
| /** |
| * Return a map of properties representing global elements keyed on QName |
| * @return a map of global elements keyed on QName |
| */ |
| private Map getGlobalElements() { |
| return globalElements; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public Map buildAppInfoMap(List appInfoElements) { |
| HashMap appInfoMap = new HashMap(); |
| |
| // Build AppInfo map |
| if (appInfoElements != null) { |
| for (int i = 0; i < appInfoElements.size(); i++) { |
| Element nextElement = (Element)appInfoElements.get(i); |
| |
| if (nextElement.getNamespaceURI().equals(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI) && nextElement.getLocalName().equals("appinfo")) { |
| String key = nextElement.getAttribute(SDOConstants.APPINFO_SOURCE_ATTRIBUTE); |
| String value = (String)appInfoMap.get(key); |
| StringWriter sw = new StringWriter(); |
| WriterRecord wrec = new WriterRecord(); |
| wrec.setWriter(sw); |
| wrec.node(nextElement, new NamespaceResolver()); |
| wrec.flush(); |
| appInfoMap.put(key, value == null ? sw.toString() : value + sw.toString()); |
| } |
| } |
| } |
| return appInfoMap; |
| } |
| |
| @Override |
| public void reset() { |
| globalAttributes = new HashMap(); |
| initOpenProps(); |
| globalElements = new HashMap(); |
| } |
| |
| @Override |
| public HelperContext getHelperContext() { |
| return aHelperContext; |
| } |
| |
| @Override |
| public void setHelperContext(HelperContext helperContext) { |
| aHelperContext = helperContext; |
| } |
| |
| public String getStringFromAppInfoElement(Element appInfo) { |
| FormattedWriterRecord record = new FormattedWriterRecord(); |
| record.setWriter(new StringWriter()); |
| record.node(appInfo, new NamespaceResolver()); |
| return record.getWriter().toString(); |
| } |
| |
| /** |
| * INTERNAL: |
| * |
| * @param qname |
| * @param prop |
| * @param isElement |
| * Register the given property with the given qname. |
| */ |
| @Override |
| public void addGlobalProperty(QName qname, Property prop, boolean isElement) { |
| ((SDOProperty)prop).setUri(qname.getNamespaceURI()); |
| if (isElement) { |
| getGlobalElements().put(qname, prop); |
| } else { |
| getGlobalAttributes().put(qname, prop); |
| } |
| } |
| } |