blob: 34f2909dca8c0374d43b7dadb50f0de45ed54e8e [file] [log] [blame]
/*
* 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);
}
}
}