You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2017/07/28 18:22:17 UTC
[02/12] incubator-juneau git commit: Allow serializer and parser
sessions to be reused.
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaDocSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaDocSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaDocSerializerSession.java
new file mode 100644
index 0000000..23429a4
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaDocSerializerSession.java
@@ -0,0 +1,53 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.xml;
+
+import org.apache.juneau.serializer.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link XmlSchemaDocSerializer}.
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused within the same thread.
+ */
+public class XmlSchemaDocSerializerSession extends XmlSchemaSerializerSession {
+
+ /**
+ * Create a new session using properties specified in the context.
+ *
+ * @param ctx
+ * The context creating this session object.
+ * The context contains all the configuration settings for this object.
+ * @param args
+ * Runtime arguments.
+ * These specify session-level information such as locale and URI context.
+ * It also include session-level properties that override the properties defined on the bean and
+ * serializer contexts.
+ * <br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}.
+ */
+ protected XmlSchemaDocSerializerSession(XmlSerializerContext ctx, SerializerSessionArgs args) {
+ super(ctx, args);
+ }
+
+ @Override /* SerializerSession */
+ protected void doSerialize(SerializerPipe out, Object o) throws Exception {
+ XmlWriter w = getXmlWriter(out);
+ w.append("<?xml")
+ .attr("version", "1.0")
+ .attr("encoding", "UTF-8")
+ .appendln("?>");
+ w.flush();
+ super.doSerialize(out, o);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
index c1af712..26ba629 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializer.java
@@ -12,26 +12,9 @@
// ***************************************************************************************************************************
package org.apache.juneau.xml;
-import static org.apache.juneau.xml.annotation.XmlFormat.*;
-import static org.apache.juneau.internal.ArrayUtils.*;
-
-
-import java.io.*;
-import java.lang.reflect.*;
-import java.util.*;
-import java.util.regex.*;
-
-import javax.xml.*;
-import javax.xml.transform.stream.*;
-import javax.xml.validation.*;
-
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
-import org.apache.juneau.http.*;
import org.apache.juneau.serializer.*;
-import org.apache.juneau.xml.annotation.*;
-import org.w3c.dom.bootstrap.*;
-import org.w3c.dom.ls.*;
/**
* Serializes POJO metadata to HTTP responses as XML.
@@ -58,540 +41,17 @@ import org.w3c.dom.ls.*;
@Produces(value="text/xml+schema",contentType="text/xml")
public class XmlSchemaSerializer extends XmlSerializer {
- private final XmlSerializerContext ctx;
-
/**
* Constructor.
*
* @param propertyStore Initialize with the specified config property store.
*/
public XmlSchemaSerializer(PropertyStore propertyStore) {
- this(propertyStore, null);
- }
-
- /**
- * Constructor
- *
- * @param propertyStore The property store containing all the settings for this object.
- * @param overrideProperties A set of overridden settings, typically defined by the class itself.
- */
- public XmlSchemaSerializer(PropertyStore propertyStore, Map<String,Object> overrideProperties) {
- super(propertyStore);
- this.ctx = this.propertyStore.create(overrideProperties).getContext(XmlSerializerContext.class);
- }
-
- @Override /* XmlSerializer */
- protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
- XmlSerializerSession s = (XmlSerializerSession)session;
-
- if (s.isEnableNamespaces() && s.isAutoDetectNamespaces())
- findNsfMappings(s, o);
-
- Namespace xs = s.getXsNamespace();
- Namespace[] allNs = append(new Namespace[]{s.getDefaultNamespace()}, s.getNamespaces());
-
- Schemas schemas = new Schemas(s, xs, s.getDefaultNamespace(), allNs);
- schemas.process(s, o);
- schemas.serializeTo(out.getWriter());
- }
-
- /**
- * Returns an XML-Schema validator based on the output returned by {@link #doSerialize(SerializerSession, SerializerOutput, Object)};
- *
- * @param session
- * The serializer session object return by {@link #createSession(ObjectMap, Method, Locale, TimeZone,
- * MediaType, UriContext)}.
- * Can be <jk>null</jk>.
- * @param out The target writer.
- * @param o The object to serialize.
- * @return The new validator.
- * @throws Exception If a problem was detected in the XML-Schema output produced by this serializer.
- */
- public Validator getValidator(SerializerSession session, SerializerOutput out, Object o) throws Exception {
- doSerialize(session, out, o);
- String xmlSchema = out.getWriter().toString();
-
- // create a SchemaFactory capable of understanding WXS schemas
- SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
-
- if (xmlSchema.indexOf('\u0000') != -1) {
-
- // Break it up into a map of namespaceURI->schema document
- final Map<String,String> schemas = new HashMap<String,String>();
- String[] ss = xmlSchema.split("\u0000");
- xmlSchema = ss[0];
- for (String s : ss) {
- Matcher m = pTargetNs.matcher(s);
- if (m.find())
- schemas.put(m.group(1), s);
- }
-
- // Create a custom resolver
- factory.setResourceResolver(
- new LSResourceResolver() {
-
- @Override /* LSResourceResolver */
- public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
-
- String schema = schemas.get(namespaceURI);
- if (schema == null)
- throw new FormattedRuntimeException("No schema found for namespaceURI ''{0}''", namespaceURI);
-
- try {
- DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
- DOMImplementationLS domImplementationLS = (DOMImplementationLS)registry.getDOMImplementation("LS 3.0");
- LSInput in = domImplementationLS.createLSInput();
- in.setCharacterStream(new StringReader(schema));
- in.setSystemId(systemId);
- return in;
-
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
- }
- );
- }
- return factory.newSchema(new StreamSource(new StringReader(xmlSchema))).newValidator();
- }
-
- private static Pattern pTargetNs = Pattern.compile("targetNamespace=['\"]([^'\"]+)['\"]");
-
-
- /* An instance of a global element, global attribute, or XML type to be serialized. */
- private static class QueueEntry {
- Namespace ns;
- String name;
- ClassMeta<?> cm;
- QueueEntry(Namespace ns, String name, ClassMeta<?> cm) {
- this.ns = ns;
- this.name = name;
- this.cm = cm;
- }
- }
-
- /* An encapsulation of all schemas present in the metamodel of the serialized object. */
- private class Schemas extends LinkedHashMap<Namespace,Schema> {
-
- private static final long serialVersionUID = 1L;
-
- private Namespace defaultNs;
- private LinkedList<QueueEntry>
- elementQueue = new LinkedList<QueueEntry>(),
- attributeQueue = new LinkedList<QueueEntry>(),
- typeQueue = new LinkedList<QueueEntry>();
-
- private Schemas(XmlSerializerSession session, Namespace xs, Namespace defaultNs, Namespace[] allNs) throws IOException {
- this.defaultNs = defaultNs;
- for (Namespace ns : allNs)
- put(ns, new Schema(session, this, xs, ns, defaultNs, allNs));
- }
-
- private Schema getSchema(Namespace ns) {
- if (ns == null)
- ns = defaultNs;
- Schema s = get(ns);
- if (s == null)
- throw new FormattedRuntimeException("No schema defined for namespace ''{0}''", ns);
- return s;
- }
-
- private void process(SerializerSession session, Object o) throws IOException {
- ClassMeta<?> cm = session.getClassMetaForObject(o);
- Namespace ns = defaultNs;
- if (cm == null)
- queueElement(ns, "null", object());
- else {
- XmlClassMeta xmlMeta = cm.getExtendedMeta(XmlClassMeta.class);
- if (cm.getDictionaryName() != null && xmlMeta.getNamespace() != null)
- ns = xmlMeta.getNamespace();
- queueElement(ns, cm.getDictionaryName(), cm);
- }
- processQueue();
- }
-
-
- private void processQueue() throws IOException {
- boolean b;
- do {
- b = false;
- while (! elementQueue.isEmpty()) {
- QueueEntry q = elementQueue.removeFirst();
- b |= getSchema(q.ns).processElement(q.name, q.cm);
- }
- while (! typeQueue.isEmpty()) {
- QueueEntry q = typeQueue.removeFirst();
- b |= getSchema(q.ns).processType(q.name, q.cm);
- }
- while (! attributeQueue.isEmpty()) {
- QueueEntry q = attributeQueue.removeFirst();
- b |= getSchema(q.ns).processAttribute(q.name, q.cm);
- }
- } while (b);
- }
-
- private void queueElement(Namespace ns, String name, ClassMeta<?> cm) {
- elementQueue.add(new QueueEntry(ns, name, cm));
- }
-
- private void queueType(Namespace ns, String name, ClassMeta<?> cm) {
- if (name == null)
- name = XmlUtils.encodeElementName(cm);
- typeQueue.add(new QueueEntry(ns, name, cm));
- }
-
- private void queueAttribute(Namespace ns, String name, ClassMeta<?> cm) {
- attributeQueue.add(new QueueEntry(ns, name, cm));
- }
-
- private void serializeTo(Writer w) throws IOException {
- boolean b = false;
- for (Schema s : values()) {
- if (b)
- w.append('\u0000');
- w.append(s.toString());
- b = true;
- }
- }
- }
-
- /* An encapsulation of a single schema. */
- private class Schema {
- private StringWriter sw = new StringWriter();
- private XmlWriter w;
- private XmlSerializerSession session;
- private Namespace defaultNs, targetNs;
- private Schemas schemas;
- private Set<String>
- processedTypes = new HashSet<String>(),
- processedAttributes = new HashSet<String>(),
- processedElements = new HashSet<String>();
-
- public Schema(XmlSerializerSession session, Schemas schemas, Namespace xs, Namespace targetNs, Namespace defaultNs, Namespace[] allNs) throws IOException {
- this.schemas = schemas;
- this.defaultNs = defaultNs;
- this.targetNs = targetNs;
- this.session = session;
- w = new XmlWriter(sw, session.isUseWhitespace(), session.getMaxIndent(), session.isTrimStrings(), session.getQuoteChar(), null, true, null);
- int i = session.getIndent();
- w.oTag(i, "schema");
- w.attr("xmlns", xs.getUri());
- w.attr("targetNamespace", targetNs.getUri());
- w.attr("elementFormDefault", "qualified");
- if (targetNs != defaultNs)
- w.attr("attributeFormDefault", "qualified");
- for (Namespace ns2 : allNs)
- w.attr("xmlns", ns2.name, ns2.uri);
- w.append('>').nl(i);
- for (Namespace ns : allNs) {
- if (ns != targetNs) {
- w.oTag(i+1, "import")
- .attr("namespace", ns.getUri())
- .attr("schemaLocation", ns.getName()+".xsd")
- .append("/>").nl(i+1);
- }
- }
- }
-
- private boolean processElement(String name, ClassMeta<?> cm) throws IOException {
- if (processedElements.contains(name))
- return false;
- processedElements.add(name);
-
- ClassMeta<?> ft = cm.getSerializedClassMeta();
- int i = session.getIndent() + 1;
- if (name == null)
- name = getElementName(ft);
- Namespace ns = first(ft.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- String type = getXmlType(ns, ft);
-
- w.oTag(i, "element")
- .attr("name", XmlUtils.encodeElementName(name))
- .attr("type", type)
- .append('/').append('>').nl(i);
-
- schemas.queueType(ns, null, ft);
- schemas.processQueue();
- return true;
- }
-
- private boolean processAttribute(String name, ClassMeta<?> cm) throws IOException {
- if (processedAttributes.contains(name))
- return false;
- processedAttributes.add(name);
-
- int i = session.getIndent() + 1;
- String type = getXmlAttrType(cm);
-
- w.oTag(i, "attribute")
- .attr("name", name)
- .attr("type", type)
- .append('/').append('>').nl(i);
-
- return true;
- }
-
- private boolean processType(String name, ClassMeta<?> cm) throws IOException {
- if (processedTypes.contains(name))
- return false;
- processedTypes.add(name);
-
- int i = session.getIndent() + 1;
-
- cm = cm.getSerializedClassMeta();
- XmlBeanMeta xbm = cm.isBean() ? cm.getBeanMeta().getExtendedMeta(XmlBeanMeta.class) : null;
-
- w.oTag(i, "complexType")
- .attr("name", name);
-
- // This element can have mixed content if:
- // 1) It's a generic Object (so it can theoretically be anything)
- // 2) The bean has a property defined with @XmlFormat.CONTENT.
- if ((xbm != null && (xbm.getContentFormat() != null && xbm.getContentFormat().isOneOf(TEXT,TEXT_PWS,MIXED,MIXED_PWS,XMLTEXT))) || ! cm.isMapOrBean())
- w.attr("mixed", "true");
-
- w.cTag().nl(i);
-
- if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
- w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl(i+1);
-
- } else {
-
- //----- Bean -----
- if (cm.isBean()) {
- BeanMeta<?> bm = cm.getBeanMeta();
-
- boolean hasChildElements = false;
-
- for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlFormat pMetaFormat = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
- if (pMetaFormat != XmlFormat.ATTR)
- hasChildElements = true;
- }
-
- XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class);
- if (xbm2.getContentProperty() != null && xbm2.getContentFormat() == ELEMENTS) {
- w.sTag(i+1, "sequence").nl(i+1);
- w.oTag(i+2, "any")
- .attr("processContents", "skip")
- .attr("minOccurs", 0)
- .ceTag().nl(i+2);
- w.eTag(i+1, "sequence").nl(i+1);
-
- } else if (hasChildElements) {
-
- boolean hasOtherNsElement = false;
- boolean hasCollapsed = false;
-
- for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- if (xmlMeta.getXmlFormat() != ATTR) {
- if (xmlMeta.getNamespace() != null) {
- ClassMeta<?> ct2 = pMeta.getClassMeta();
- Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- // Child element is in another namespace.
- schemas.queueElement(cNs, pMeta.getName(), ct2);
- hasOtherNsElement = true;
- }
- if (xmlMeta.getXmlFormat() == COLLAPSED)
- hasCollapsed = true;
- }
- }
-
- if (hasOtherNsElement || hasCollapsed) {
- // If this bean has any child elements in another namespace,
- // we need to add an <any> element.
- w.oTag(i+1, "choice").attr("maxOccurs", "unbounded").cTag().nl(i+1);
- w.oTag(i+2, "any")
- .attr("processContents", "skip")
- .attr("minOccurs", 0)
- .ceTag().nl(i+2);
- w.eTag(i+1, "choice").nl(i+1);
-
- } else {
- w.sTag(i+1, "all").nl(i+1);
- for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
- XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- if (xmlMeta.getXmlFormat() != ATTR) {
- boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
- ClassMeta<?> ct2 = pMeta.getClassMeta();
- String childName = pMeta.getName();
- if (isCollapsed) {
- if (xmlMeta.getChildName() != null)
- childName = xmlMeta.getChildName();
- ct2 = pMeta.getClassMeta().getElementType();
- }
- Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- if (xmlMeta.getNamespace() == null) {
- w.oTag(i+2, "element")
- .attr("name", XmlUtils.encodeElementName(childName), false)
- .attr("type", getXmlType(cNs, ct2))
- .attr("minOccurs", 0);
-
- w.ceTag().nl(i+2);
- } else {
- // Child element is in another namespace.
- schemas.queueElement(cNs, pMeta.getName(), ct2);
- hasOtherNsElement = true;
- }
-
- }
- }
- w.eTag(i+1, "all").nl(i+1);
- }
-
- }
-
- for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) {
- Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
- if (pNs == null)
- pNs = defaultNs;
-
- // If the bean attribute has a different namespace than the bean, then it needs to
- // be added as a top-level entry in the appropriate schema file.
- if (pNs != targetNs) {
- schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
- w.oTag(i+1, "attribute")
- //.attr("name", pMeta.getName(), true)
- .attr("ref", pNs.getName() + ':' + pMeta.getName())
- .ceTag().nl(i+1);
- }
-
- // Otherwise, it's just a plain attribute of this bean.
- else {
- w.oTag(i+1, "attribute")
- .attr("name", pMeta.getName(), true)
- .attr("type", getXmlAttrType(pMeta.getClassMeta()))
- .ceTag().nl(i+1);
- }
- }
-
- //----- Collection -----
- } else if (cm.isCollectionOrArray()) {
- ClassMeta<?> elementType = cm.getElementType();
- if (elementType.isObject()) {
- w.sTag(i+1, "sequence").nl(i+1);
- w.oTag(i+2, "any")
- .attr("processContents", "skip")
- .attr("maxOccurs", "unbounded")
- .attr("minOccurs", "0")
- .ceTag().nl(i+2);
- w.eTag(i+1, "sequence").nl(i+1);
- } else {
- Namespace cNs = first(elementType.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
- schemas.queueType(cNs, null, elementType);
- w.sTag(i+1, "sequence").nl(i+1);
- w.oTag(i+2, "any")
- .attr("processContents", "skip")
- .attr("maxOccurs", "unbounded")
- .attr("minOccurs", "0")
- .ceTag().nl(i+2);
- w.eTag(i+1, "sequence").nl(i+1);
- }
-
- //----- Map -----
- } else if (cm.isMap() || cm.isAbstract() || cm.isObject()) {
- w.sTag(i+1, "sequence").nl(i+1);
- w.oTag(i+2, "any")
- .attr("processContents", "skip")
- .attr("maxOccurs", "unbounded")
- .attr("minOccurs", "0")
- .ceTag().nl(i+2);
- w.eTag(i+1, "sequence").nl(i+1);
- }
-
- w.oTag(i+1, "attribute")
- .attr("name", session.getBeanTypePropertyName(null))
- .attr("type", "string")
- .ceTag().nl(i+1);
- }
-
- w.eTag(i, "complexType").nl(i);
- schemas.processQueue();
-
- return true;
- }
-
- private String getElementName(ClassMeta<?> cm) {
- cm = cm.getSerializedClassMeta();
- String name = cm.getDictionaryName();
-
- if (name == null) {
- if (cm.isBoolean())
- name = "boolean";
- else if (cm.isNumber())
- name = "number";
- else if (cm.isCollectionOrArray())
- name = "array";
- else if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || cm.isObject() || cm.isAbstract()))
- name = "string";
- else
- name = "object";
- }
- return name;
- }
-
- @Override /* Object */
- public String toString() {
- try {
- int i = session.getIndent();
- w.eTag(i, "schema").nl(i);
- } catch (IOException e) {
- throw new RuntimeException(e); // Shouldn't happen.
- }
- return sw.toString();
- }
-
- private String getXmlType(Namespace currentNs, ClassMeta<?> cm) {
- String name = null;
- cm = cm.getSerializedClassMeta();
- if (currentNs == targetNs) {
- if (cm.isPrimitive()) {
- if (cm.isBoolean())
- name = "boolean";
- else if (cm.isNumber()) {
- if (cm.isDecimal())
- name = "decimal";
- else
- name = "integer";
- }
- }
- }
- if (name == null) {
- name = XmlUtils.encodeElementName(cm);
- schemas.queueType(currentNs, name, cm);
- return currentNs.getName() + ":" + name;
- }
-
- return name;
- }
- }
-
- private static <T> T first(T...tt) {
- for (T t : tt)
- if (t != null)
- return t;
- return null;
- }
-
-
- private static String getXmlAttrType(ClassMeta<?> cm) {
- if (cm.isBoolean())
- return "boolean";
- if (cm.isNumber()) {
- if (cm.isDecimal())
- return "decimal";
- return "integer";
- }
- return "string";
+ super(propertyStore.copy().append(XmlSerializerContext.XML_enableNamespaces, true));
}
@Override /* Serializer */
- public XmlSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale, TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
- // This serializer must always have namespaces enabled.
- if (op == null)
- op = new ObjectMap();
- op.put(XmlSerializerContext.XML_enableNamespaces, true);
- return new XmlSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
+ public WriterSerializerSession createSession(SerializerSessionArgs args) {
+ return new XmlSchemaSerializerSession(ctx, args);
}
}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
new file mode 100644
index 0000000..f3b9120
--- /dev/null
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSchemaSerializerSession.java
@@ -0,0 +1,551 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
+// * with the License. You may obtain a copy of the License at *
+// * *
+// * http://www.apache.org/licenses/LICENSE-2.0 *
+// * *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
+// * specific language governing permissions and limitations under the License. *
+// ***************************************************************************************************************************
+package org.apache.juneau.xml;
+
+import static org.apache.juneau.internal.ArrayUtils.*;
+import static org.apache.juneau.xml.annotation.XmlFormat.*;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.xml.*;
+import javax.xml.transform.stream.*;
+import javax.xml.validation.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.serializer.*;
+import org.apache.juneau.xml.annotation.*;
+import org.w3c.dom.bootstrap.*;
+import org.w3c.dom.ls.*;
+
+/**
+ * Session object that lives for the duration of a single use of {@link XmlSchemaSerializer}.
+ *
+ * <p>
+ * This class is NOT thread safe.
+ * It is typically discarded after one-time use although it can be reused within the same thread.
+ */
+public class XmlSchemaSerializerSession extends XmlSerializerSession {
+
+ /**
+ * Create a new session using properties specified in the context.
+ *
+ * @param ctx
+ * The context creating this session object.
+ * The context contains all the configuration settings for this object.
+ * @param args
+ * Runtime arguments.
+ * These specify session-level information such as locale and URI context.
+ * It also include session-level properties that override the properties defined on the bean and
+ * serializer contexts.
+ * <br>If <jk>null</jk>, defaults to {@link SerializerSessionArgs#DEFAULT}.
+ */
+ protected XmlSchemaSerializerSession(XmlSerializerContext ctx, SerializerSessionArgs args) {
+ super(ctx, args);
+ }
+
+ @Override /* SerializerSession */
+ protected void doSerialize(SerializerPipe out, Object o) throws Exception {
+ if (enableNamespaces && autoDetectNamespaces)
+ findNsfMappings(o);
+
+ Namespace xs = xsNamespace;
+ Namespace[] allNs = append(new Namespace[]{defaultNamespace}, namespaces);
+
+ Schemas schemas = new Schemas(xs, defaultNamespace, allNs);
+ schemas.process(o);
+ schemas.serializeTo(out.getWriter());
+ }
+
+ /**
+ * Returns an XML-Schema validator based on the output returned by {@link #doSerialize(SerializerPipe, Object)};
+ *
+ * @param out The target writer.
+ * @param o The object to serialize.
+ * @return The new validator.
+ * @throws Exception If a problem was detected in the XML-Schema output produced by this serializer.
+ */
+ public Validator getValidator(SerializerPipe out, Object o) throws Exception {
+ doSerialize(out, o);
+ String xmlSchema = out.getWriter().toString();
+
+ // create a SchemaFactory capable of understanding WXS schemas
+ SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+
+ if (xmlSchema.indexOf('\u0000') != -1) {
+
+ // Break it up into a map of namespaceURI->schema document
+ final Map<String,String> schemas = new HashMap<String,String>();
+ String[] ss = xmlSchema.split("\u0000");
+ xmlSchema = ss[0];
+ for (String s : ss) {
+ Matcher m = pTargetNs.matcher(s);
+ if (m.find())
+ schemas.put(m.group(1), s);
+ }
+
+ // Create a custom resolver
+ factory.setResourceResolver(
+ new LSResourceResolver() {
+
+ @Override /* LSResourceResolver */
+ public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
+
+ String schema = schemas.get(namespaceURI);
+ if (schema == null)
+ throw new FormattedRuntimeException("No schema found for namespaceURI ''{0}''", namespaceURI);
+
+ try {
+ DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
+ DOMImplementationLS domImplementationLS = (DOMImplementationLS)registry.getDOMImplementation("LS 3.0");
+ LSInput in = domImplementationLS.createLSInput();
+ in.setCharacterStream(new StringReader(schema));
+ in.setSystemId(systemId);
+ return in;
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ );
+ }
+ return factory.newSchema(new StreamSource(new StringReader(xmlSchema))).newValidator();
+ }
+
+ private static Pattern pTargetNs = Pattern.compile("targetNamespace=['\"]([^'\"]+)['\"]");
+
+
+ /* An instance of a global element, global attribute, or XML type to be serialized. */
+ private static class QueueEntry {
+ Namespace ns;
+ String name;
+ ClassMeta<?> cm;
+ QueueEntry(Namespace ns, String name, ClassMeta<?> cm) {
+ this.ns = ns;
+ this.name = name;
+ this.cm = cm;
+ }
+ }
+
+ /* An encapsulation of all schemas present in the metamodel of the serialized object. */
+ private class Schemas extends LinkedHashMap<Namespace,Schema> {
+
+ private static final long serialVersionUID = 1L;
+
+ private Namespace defaultNs;
+ private LinkedList<QueueEntry>
+ elementQueue = new LinkedList<QueueEntry>(),
+ attributeQueue = new LinkedList<QueueEntry>(),
+ typeQueue = new LinkedList<QueueEntry>();
+
+ private Schemas(Namespace xs, Namespace defaultNs, Namespace[] allNs) throws IOException {
+ this.defaultNs = defaultNs;
+ for (Namespace ns : allNs)
+ put(ns, new Schema(this, xs, ns, defaultNs, allNs));
+ }
+
+ private Schema getSchema(Namespace ns) {
+ if (ns == null)
+ ns = defaultNs;
+ Schema s = get(ns);
+ if (s == null)
+ throw new FormattedRuntimeException("No schema defined for namespace ''{0}''", ns);
+ return s;
+ }
+
+ private void process(Object o) throws IOException {
+ ClassMeta<?> cm = getClassMetaForObject(o);
+ Namespace ns = defaultNs;
+ if (cm == null)
+ queueElement(ns, "null", object());
+ else {
+ XmlClassMeta xmlMeta = cm.getExtendedMeta(XmlClassMeta.class);
+ if (cm.getDictionaryName() != null && xmlMeta.getNamespace() != null)
+ ns = xmlMeta.getNamespace();
+ queueElement(ns, cm.getDictionaryName(), cm);
+ }
+ processQueue();
+ }
+
+
+ private void processQueue() throws IOException {
+ boolean b;
+ do {
+ b = false;
+ while (! elementQueue.isEmpty()) {
+ QueueEntry q = elementQueue.removeFirst();
+ b |= getSchema(q.ns).processElement(q.name, q.cm);
+ }
+ while (! typeQueue.isEmpty()) {
+ QueueEntry q = typeQueue.removeFirst();
+ b |= getSchema(q.ns).processType(q.name, q.cm);
+ }
+ while (! attributeQueue.isEmpty()) {
+ QueueEntry q = attributeQueue.removeFirst();
+ b |= getSchema(q.ns).processAttribute(q.name, q.cm);
+ }
+ } while (b);
+ }
+
+ private void queueElement(Namespace ns, String name, ClassMeta<?> cm) {
+ elementQueue.add(new QueueEntry(ns, name, cm));
+ }
+
+ private void queueType(Namespace ns, String name, ClassMeta<?> cm) {
+ if (name == null)
+ name = XmlUtils.encodeElementName(cm);
+ typeQueue.add(new QueueEntry(ns, name, cm));
+ }
+
+ private void queueAttribute(Namespace ns, String name, ClassMeta<?> cm) {
+ attributeQueue.add(new QueueEntry(ns, name, cm));
+ }
+
+ private void serializeTo(Writer w) throws IOException {
+ boolean b = false;
+ for (Schema s : values()) {
+ if (b)
+ w.append('\u0000');
+ w.append(s.toString());
+ b = true;
+ }
+ }
+ }
+
+ /* An encapsulation of a single schema. */
+ private class Schema {
+ private StringWriter sw = new StringWriter();
+ private XmlWriter w;
+ private Namespace defaultNs, targetNs;
+ private Schemas schemas;
+ private Set<String>
+ processedTypes = new HashSet<String>(),
+ processedAttributes = new HashSet<String>(),
+ processedElements = new HashSet<String>();
+
+ public Schema(Schemas schemas, Namespace xs, Namespace targetNs, Namespace defaultNs, Namespace[] allNs) throws IOException {
+ this.schemas = schemas;
+ this.defaultNs = defaultNs;
+ this.targetNs = targetNs;
+ w = new XmlWriter(sw, isUseWhitespace(), getMaxIndent(), isTrimStrings(), getQuoteChar(), null, true, null);
+ int i = indent;
+ w.oTag(i, "schema");
+ w.attr("xmlns", xs.getUri());
+ w.attr("targetNamespace", targetNs.getUri());
+ w.attr("elementFormDefault", "qualified");
+ if (targetNs != defaultNs)
+ w.attr("attributeFormDefault", "qualified");
+ for (Namespace ns2 : allNs)
+ w.attr("xmlns", ns2.name, ns2.uri);
+ w.append('>').nl(i);
+ for (Namespace ns : allNs) {
+ if (ns != targetNs) {
+ w.oTag(i+1, "import")
+ .attr("namespace", ns.getUri())
+ .attr("schemaLocation", ns.getName()+".xsd")
+ .append("/>").nl(i+1);
+ }
+ }
+ }
+
+ private boolean processElement(String name, ClassMeta<?> cm) throws IOException {
+ if (processedElements.contains(name))
+ return false;
+ processedElements.add(name);
+
+ ClassMeta<?> ft = cm.getSerializedClassMeta();
+ if (name == null)
+ name = getElementName(ft);
+ Namespace ns = first(ft.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ String type = getXmlType(ns, ft);
+
+ w.oTag(indent+1, "element")
+ .attr("name", XmlUtils.encodeElementName(name))
+ .attr("type", type)
+ .append('/').append('>').nl(indent+1);
+
+ schemas.queueType(ns, null, ft);
+ schemas.processQueue();
+ return true;
+ }
+
+ private boolean processAttribute(String name, ClassMeta<?> cm) throws IOException {
+ if (processedAttributes.contains(name))
+ return false;
+ processedAttributes.add(name);
+
+ String type = getXmlAttrType(cm);
+
+ w.oTag(indent+1, "attribute")
+ .attr("name", name)
+ .attr("type", type)
+ .append('/').append('>').nl(indent+1);
+
+ return true;
+ }
+
+ private boolean processType(String name, ClassMeta<?> cm) throws IOException {
+ if (processedTypes.contains(name))
+ return false;
+ processedTypes.add(name);
+
+ int i = indent + 1;
+
+ cm = cm.getSerializedClassMeta();
+ XmlBeanMeta xbm = cm.isBean() ? cm.getBeanMeta().getExtendedMeta(XmlBeanMeta.class) : null;
+
+ w.oTag(i, "complexType")
+ .attr("name", name);
+
+ // This element can have mixed content if:
+ // 1) It's a generic Object (so it can theoretically be anything)
+ // 2) The bean has a property defined with @XmlFormat.CONTENT.
+ if ((xbm != null && (xbm.getContentFormat() != null && xbm.getContentFormat().isOneOf(TEXT,TEXT_PWS,MIXED,MIXED_PWS,XMLTEXT))) || ! cm.isMapOrBean())
+ w.attr("mixed", "true");
+
+ w.cTag().nl(i);
+
+ if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) {
+ w.oTag(i+1, "attribute").attr("name", getBeanTypePropertyName(cm)).attr("type", "string").ceTag().nl(i+1);
+
+ } else {
+
+ //----- Bean -----
+ if (cm.isBean()) {
+ BeanMeta<?> bm = cm.getBeanMeta();
+
+ boolean hasChildElements = false;
+
+ for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
+ XmlFormat pMetaFormat = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat();
+ if (pMetaFormat != XmlFormat.ATTR)
+ hasChildElements = true;
+ }
+
+ XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class);
+ if (xbm2.getContentProperty() != null && xbm2.getContentFormat() == ELEMENTS) {
+ w.sTag(i+1, "sequence").nl(i+1);
+ w.oTag(i+2, "any")
+ .attr("processContents", "skip")
+ .attr("minOccurs", 0)
+ .ceTag().nl(i+2);
+ w.eTag(i+1, "sequence").nl(i+1);
+
+ } else if (hasChildElements) {
+
+ boolean hasOtherNsElement = false;
+ boolean hasCollapsed = false;
+
+ for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
+ XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+ if (xmlMeta.getXmlFormat() != ATTR) {
+ if (xmlMeta.getNamespace() != null) {
+ ClassMeta<?> ct2 = pMeta.getClassMeta();
+ Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ // Child element is in another namespace.
+ schemas.queueElement(cNs, pMeta.getName(), ct2);
+ hasOtherNsElement = true;
+ }
+ if (xmlMeta.getXmlFormat() == COLLAPSED)
+ hasCollapsed = true;
+ }
+ }
+
+ if (hasOtherNsElement || hasCollapsed) {
+ // If this bean has any child elements in another namespace,
+ // we need to add an <any> element.
+ w.oTag(i+1, "choice").attr("maxOccurs", "unbounded").cTag().nl(i+1);
+ w.oTag(i+2, "any")
+ .attr("processContents", "skip")
+ .attr("minOccurs", 0)
+ .ceTag().nl(i+2);
+ w.eTag(i+1, "choice").nl(i+1);
+
+ } else {
+ w.sTag(i+1, "all").nl(i+1);
+ for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) {
+ XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
+ if (xmlMeta.getXmlFormat() != ATTR) {
+ boolean isCollapsed = xmlMeta.getXmlFormat() == COLLAPSED;
+ ClassMeta<?> ct2 = pMeta.getClassMeta();
+ String childName = pMeta.getName();
+ if (isCollapsed) {
+ if (xmlMeta.getChildName() != null)
+ childName = xmlMeta.getChildName();
+ ct2 = pMeta.getClassMeta().getElementType();
+ }
+ Namespace cNs = first(xmlMeta.getNamespace(), ct2.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ if (xmlMeta.getNamespace() == null) {
+ w.oTag(i+2, "element")
+ .attr("name", XmlUtils.encodeElementName(childName), false)
+ .attr("type", getXmlType(cNs, ct2))
+ .attr("minOccurs", 0);
+
+ w.ceTag().nl(i+2);
+ } else {
+ // Child element is in another namespace.
+ schemas.queueElement(cNs, pMeta.getName(), ct2);
+ hasOtherNsElement = true;
+ }
+
+ }
+ }
+ w.eTag(i+1, "all").nl(i+1);
+ }
+
+ }
+
+ for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) {
+ Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
+ if (pNs == null)
+ pNs = defaultNs;
+
+ // If the bean attribute has a different namespace than the bean, then it needs to
+ // be added as a top-level entry in the appropriate schema file.
+ if (pNs != targetNs) {
+ schemas.queueAttribute(pNs, pMeta.getName(), pMeta.getClassMeta());
+ w.oTag(i+1, "attribute")
+ //.attr("name", pMeta.getName(), true)
+ .attr("ref", pNs.getName() + ':' + pMeta.getName())
+ .ceTag().nl(i+1);
+ }
+
+ // Otherwise, it's just a plain attribute of this bean.
+ else {
+ w.oTag(i+1, "attribute")
+ .attr("name", pMeta.getName(), true)
+ .attr("type", getXmlAttrType(pMeta.getClassMeta()))
+ .ceTag().nl(i+1);
+ }
+ }
+
+ //----- Collection -----
+ } else if (cm.isCollectionOrArray()) {
+ ClassMeta<?> elementType = cm.getElementType();
+ if (elementType.isObject()) {
+ w.sTag(i+1, "sequence").nl(i+1);
+ w.oTag(i+2, "any")
+ .attr("processContents", "skip")
+ .attr("maxOccurs", "unbounded")
+ .attr("minOccurs", "0")
+ .ceTag().nl(i+2);
+ w.eTag(i+1, "sequence").nl(i+1);
+ } else {
+ Namespace cNs = first(elementType.getExtendedMeta(XmlClassMeta.class).getNamespace(), cm.getExtendedMeta(XmlClassMeta.class).getNamespace(), defaultNs);
+ schemas.queueType(cNs, null, elementType);
+ w.sTag(i+1, "sequence").nl(i+1);
+ w.oTag(i+2, "any")
+ .attr("processContents", "skip")
+ .attr("maxOccurs", "unbounded")
+ .attr("minOccurs", "0")
+ .ceTag().nl(i+2);
+ w.eTag(i+1, "sequence").nl(i+1);
+ }
+
+ //----- Map -----
+ } else if (cm.isMap() || cm.isAbstract() || cm.isObject()) {
+ w.sTag(i+1, "sequence").nl(i+1);
+ w.oTag(i+2, "any")
+ .attr("processContents", "skip")
+ .attr("maxOccurs", "unbounded")
+ .attr("minOccurs", "0")
+ .ceTag().nl(i+2);
+ w.eTag(i+1, "sequence").nl(i+1);
+ }
+
+ w.oTag(i+1, "attribute")
+ .attr("name", getBeanTypePropertyName(null))
+ .attr("type", "string")
+ .ceTag().nl(i+1);
+ }
+
+ w.eTag(i, "complexType").nl(i);
+ schemas.processQueue();
+
+ return true;
+ }
+
+ private String getElementName(ClassMeta<?> cm) {
+ cm = cm.getSerializedClassMeta();
+ String name = cm.getDictionaryName();
+
+ if (name == null) {
+ if (cm.isBoolean())
+ name = "boolean";
+ else if (cm.isNumber())
+ name = "number";
+ else if (cm.isCollectionOrArray())
+ name = "array";
+ else if (! (cm.isMapOrBean() || cm.isCollectionOrArray() || cm.isObject() || cm.isAbstract()))
+ name = "string";
+ else
+ name = "object";
+ }
+ return name;
+ }
+
+ @Override /* Object */
+ public String toString() {
+ try {
+ w.eTag(indent, "schema").nl(indent);
+ } catch (IOException e) {
+ throw new RuntimeException(e); // Shouldn't happen.
+ }
+ return sw.toString();
+ }
+
+ private String getXmlType(Namespace currentNs, ClassMeta<?> cm) {
+ String name = null;
+ cm = cm.getSerializedClassMeta();
+ if (currentNs == targetNs) {
+ if (cm.isPrimitive()) {
+ if (cm.isBoolean())
+ name = "boolean";
+ else if (cm.isNumber()) {
+ if (cm.isDecimal())
+ name = "decimal";
+ else
+ name = "integer";
+ }
+ }
+ }
+ if (name == null) {
+ name = XmlUtils.encodeElementName(cm);
+ schemas.queueType(currentNs, name, cm);
+ return currentNs.getName() + ":" + name;
+ }
+
+ return name;
+ }
+ }
+
+ private static <T> T first(T...tt) {
+ for (T t : tt)
+ if (t != null)
+ return t;
+ return null;
+ }
+
+ private static String getXmlAttrType(ClassMeta<?> cm) {
+ if (cm.isBoolean())
+ return "boolean";
+ if (cm.isNumber()) {
+ if (cm.isDecimal())
+ return "decimal";
+ return "integer";
+ }
+ return "string";
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2a37f310/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
----------------------------------------------------------------------
diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
index b50dfae..e836a67 100644
--- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
+++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializer.java
@@ -13,21 +13,12 @@
package org.apache.juneau.xml;
import static org.apache.juneau.serializer.SerializerContext.*;
-import static org.apache.juneau.xml.XmlSerializer.ContentResult.*;
-import static org.apache.juneau.xml.XmlSerializer.JsonType.*;
import static org.apache.juneau.xml.XmlSerializerContext.*;
-import static org.apache.juneau.xml.annotation.XmlFormat.*;
-
-import java.lang.reflect.*;
-import java.util.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
-import org.apache.juneau.http.*;
import org.apache.juneau.json.*;
import org.apache.juneau.serializer.*;
-import org.apache.juneau.transform.*;
-import org.apache.juneau.xml.annotation.*;
/**
* Serializes POJO models to XML.
@@ -130,7 +121,6 @@ import org.apache.juneau.xml.annotation.*;
* <li>{@link SqReadable} - Default serializer, single quotes, whitespace added.
* </ul>
*/
-@SuppressWarnings({ "rawtypes", "unchecked" })
@Produces("text/xml")
public class XmlSerializer extends WriterSerializer {
@@ -162,12 +152,7 @@ public class XmlSerializer extends WriterSerializer {
* @param propertyStore The property store containing all the settings for this object.
*/
public Sq(PropertyStore propertyStore) {
- super(propertyStore);
- }
-
- @Override /* CoreObject */
- protected ObjectMap getOverrideProperties() {
- return super.getOverrideProperties().append(SERIALIZER_quoteChar, '\'');
+ super(propertyStore.copy().append(SERIALIZER_quoteChar, '\''));
}
}
@@ -180,12 +165,7 @@ public class XmlSerializer extends WriterSerializer {
* @param propertyStore The property store containing all the settings for this object.
*/
public SqReadable(PropertyStore propertyStore) {
- super(propertyStore);
- }
-
- @Override /* CoreObject */
- protected ObjectMap getOverrideProperties() {
- return super.getOverrideProperties().append(SERIALIZER_quoteChar, '\'').append(SERIALIZER_useWhitespace, true);
+ super(propertyStore.copy().append(SERIALIZER_quoteChar, '\'').append(SERIALIZER_useWhitespace, true));
}
}
@@ -199,12 +179,7 @@ public class XmlSerializer extends WriterSerializer {
* @param propertyStore The property store containing all the settings for this object.
*/
public Ns(PropertyStore propertyStore) {
- super(propertyStore);
- }
-
- @Override /* CoreObject */
- protected ObjectMap getOverrideProperties() {
- return super.getOverrideProperties().append(XML_enableNamespaces, true);
+ super(propertyStore.copy().append(XML_enableNamespaces, true));
}
}
@@ -217,12 +192,7 @@ public class XmlSerializer extends WriterSerializer {
* @param propertyStore The property store containing all the settings for this object.
*/
public NsSq(PropertyStore propertyStore) {
- super(propertyStore);
- }
-
- @Override /* CoreObject */
- protected ObjectMap getOverrideProperties() {
- return super.getOverrideProperties().append(XML_enableNamespaces, true).append(SERIALIZER_quoteChar, '\'');
+ super(propertyStore.copy().append(XML_enableNamespaces, true).append(SERIALIZER_quoteChar, '\''));
}
}
@@ -235,18 +205,13 @@ public class XmlSerializer extends WriterSerializer {
* @param propertyStore The property store containing all the settings for this object.
*/
public NsSqReadable(PropertyStore propertyStore) {
- super(propertyStore);
- }
-
- @Override /* CoreObject */
- protected ObjectMap getOverrideProperties() {
- return super.getOverrideProperties().append(XML_enableNamespaces, true).append(SERIALIZER_quoteChar, '\'')
- .append(SERIALIZER_useWhitespace, true);
+ super(propertyStore.copy().append(XML_enableNamespaces, true).append(SERIALIZER_quoteChar, '\'')
+ .append(SERIALIZER_useWhitespace, true));
}
}
- private final XmlSerializerContext ctx;
+ final XmlSerializerContext ctx;
private volatile XmlSchemaSerializer schemaSerializer;
/**
@@ -264,536 +229,6 @@ public class XmlSerializer extends WriterSerializer {
return new XmlSerializerBuilder(propertyStore);
}
- /**
- * Recursively searches for the XML namespaces on the specified POJO and adds them to the serializer context object.
- *
- * @param session The context that exists for the duration of a single serialization.
- * @param o The POJO to check.
- * @throws SerializeException
- */
- protected void findNsfMappings(XmlSerializerSession session, Object o) throws SerializeException {
- ClassMeta<?> aType = null; // The actual type
- aType = session.push(null, o, null);
-
- if (aType != null) {
- Namespace ns = aType.getExtendedMeta(XmlClassMeta.class).getNamespace();
- if (ns != null) {
- if (ns.uri != null)
- session.addNamespace(ns);
- else
- ns = null;
- }
- }
-
- // Handle recursion
- if (aType != null && ! aType.isPrimitive()) {
-
- BeanMap<?> bm = null;
- if (aType.isBeanMap()) {
- bm = (BeanMap)o;
- } else if (aType.isBean()) {
- bm = session.toBeanMap(o);
- } else if (aType.isDelegate()) {
- ClassMeta<?> innerType = ((Delegate)o).getClassMeta();
- Namespace ns = innerType.getExtendedMeta(XmlClassMeta.class).getNamespace();
- if (ns != null) {
- if (ns.uri != null)
- session.addNamespace(ns);
- else
- ns = null;
- }
-
- if (innerType.isBean()) {
- for (BeanPropertyMeta bpm : innerType.getBeanMeta().getPropertyMetas()) {
- ns = bpm.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
- if (ns != null && ns.uri != null)
- session.addNamespace(ns);
- }
-
- } else if (innerType.isMap()) {
- for (Object o2 : ((Map)o).values())
- findNsfMappings(session, o2);
- } else if (innerType.isCollection()) {
- for (Object o2 : ((Collection)o))
- findNsfMappings(session, o2);
- }
-
- } else if (aType.isMap()) {
- for (Object o2 : ((Map)o).values())
- findNsfMappings(session, o2);
- } else if (aType.isCollection()) {
- for (Object o2 : ((Collection)o))
- findNsfMappings(session, o2);
- } else if (aType.isArray() && ! aType.getElementType().isPrimitive()) {
- for (Object o2 : ((Object[])o))
- findNsfMappings(session, o2);
- }
- if (bm != null) {
- for (BeanPropertyValue p : bm.getValues(session.isTrimNulls())) {
-
- Namespace ns = p.getMeta().getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace();
- if (ns != null && ns.uri != null)
- session.addNamespace(ns);
-
- try {
- findNsfMappings(session, p.getValue());
- } catch (Throwable x) {
- // Ignore
- }
- }
- }
- }
-
- session.pop();
- }
-
- /**
- * Workhorse method.
- *
- * @param session The serializer context.
- * @param out The writer to send the output to.
- * @param o The object to serialize.
- * @param eType The expected type if this is a bean property value being serialized.
- * @param elementName The root element name.
- * @param elementNamespace The namespace of the element.
- * @param addNamespaceUris Flag indicating that namespace URIs need to be added.
- * @param format The format to serialize the output to.
- * @param isMixed We're serializing mixed content, so don't use whitespace.
- * @param preserveWhitespace
- * <jk>true</jk> if we're serializing {@link XmlFormat#MIXED_PWS} or {@link XmlFormat#TEXT_PWS}.
- * @param pMeta The bean property metadata if this is a bean property being serialized.
- * @return The same writer passed in so that calls to the writer can be chained.
- * @throws Exception If a problem occurred trying to convert the output.
- */
- protected XmlWriter serializeAnything(
- XmlSerializerSession session,
- XmlWriter out,
- Object o,
- ClassMeta eType,
- String elementName,
- Namespace elementNamespace,
- boolean addNamespaceUris,
- XmlFormat format,
- boolean isMixed,
- boolean preserveWhitespace,
- BeanPropertyMeta pMeta) throws Exception {
-
- JsonType type = null; // The type string (e.g. <type> or <x x='type'>
- int indent = isMixed ? 0 : session.indent; // Current indentation
- ClassMeta<?> aType = null; // The actual type
- ClassMeta<?> wType = null; // The wrapped type (delegate)
- ClassMeta<?> sType = object(); // The serialized type
-
- aType = session.push(elementName, o, eType);
-
- if (eType == null)
- eType = object();
-
- // Handle recursion
- if (aType == null) {
- o = null;
- aType = object();
- }
-
- if (o != null) {
-
- if (aType.isDelegate()) {
- wType = aType;
- eType = aType = ((Delegate)o).getClassMeta();
- }
-
- sType = aType.getSerializedClassMeta();
-
- // Swap if necessary
- PojoSwap swap = aType.getPojoSwap();
- if (swap != null) {
- o = swap.swap(session, o);
-
- // If the getSwapClass() method returns Object, we need to figure out
- // the actual type now.
- if (sType.isObject())
- sType = session.getClassMetaForObject(o);
- }
- } else {
- sType = eType.getSerializedClassMeta();
- }
-
- // Does the actual type match the expected type?
- boolean isExpectedType = true;
- if (o == null || ! eType.same(aType)) {
- if (eType.isNumber())
- isExpectedType = aType.isNumber();
- else if (eType.isMap())
- isExpectedType = aType.isMap();
- else if (eType.isCollectionOrArray())
- isExpectedType = aType.isCollectionOrArray();
- else
- isExpectedType = false;
- }
-
- String resolvedDictionaryName = isExpectedType ? null : aType.getDictionaryName();
-
- // Note that the dictionary name may be specified on the actual type or the serialized type.
- // HTML templates will have them defined on the serialized type.
- String dictionaryName = aType.getDictionaryName();
- if (dictionaryName == null)
- dictionaryName = sType.getDictionaryName();
-
- // char '\0' is interpreted as null.
- if (o != null && sType.isChar() && ((Character)o).charValue() == 0)
- o = null;
-
- boolean isCollapsed = false; // If 'true', this is a collection and we're not rendering the outer element.
-
- // Get the JSON type string.
- if (o == null) {
- type = NULL;
- } else if (sType.isCharSequence() || sType.isChar()) {
- type = STRING;
- } else if (sType.isNumber()) {
- type = NUMBER;
- } else if (sType.isBoolean()) {
- type = BOOLEAN;
- } else if (sType.isMapOrBean()) {
- isCollapsed = sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED;
- type = OBJECT;
- } else if (sType.isCollectionOrArray()) {
- isCollapsed = (format == COLLAPSED && ! addNamespaceUris);
- type = ARRAY;
- } else {
- type = STRING;
- }
-
- if (format.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT) && type.isOneOf(NULL,STRING,NUMBER,BOOLEAN))
- isCollapsed = true;
-
- // Is there a name associated with this bean?
- if (elementName == null && dictionaryName != null) {
- elementName = dictionaryName;
- isExpectedType = true;
- }
-
- if (session.isEnableNamespaces()) {
- if (elementNamespace == null)
- elementNamespace = sType.getExtendedMeta(XmlClassMeta.class).getNamespace();
- if (elementNamespace == null)
- elementNamespace = aType.getExtendedMeta(XmlClassMeta.class).getNamespace();
- if (elementNamespace != null && elementNamespace.uri == null)
- elementNamespace = null;
- if (elementNamespace == null)
- elementNamespace = session.getDefaultNamespace();
- } else {
- elementNamespace = null;
- }
-
- // Do we need a carriage return after the start tag?
- boolean cr = o != null && (sType.isMapOrBean() || sType.isCollectionOrArray()) && ! isMixed;
-
- String en = elementName;
- if (en == null) {
- en = type.toString();
- type = null;
- }
- boolean encodeEn = elementName != null;
- String ns = (elementNamespace == null ? null : elementNamespace.name);
- String dns = null, elementNs = null;
- if (session.isEnableNamespaces()) {
- dns = elementName == null && session.getDefaultNamespace() != null ? session.getDefaultNamespace().name : null;
- elementNs = elementName == null ? dns : ns;
- if (elementName == null)
- elementNamespace = null;
- }
-
- // Render the start tag.
- if (! isCollapsed) {
- out.oTag(indent, elementNs, en, encodeEn);
- if (addNamespaceUris) {
- out.attr((String)null, "xmlns", session.getDefaultNamespace().getUri());
-
- for (Namespace n : session.getNamespaces())
- out.attr("xmlns", n.getName(), n.getUri());
- }
- if (! isExpectedType) {
- if (resolvedDictionaryName != null)
- out.attr(dns, session.getBeanTypePropertyName(eType), resolvedDictionaryName);
- else if (type != null && type != STRING)
- out.attr(dns, session.getBeanTypePropertyName(eType), type);
- }
- if (o == null) {
- if ((sType.isBoolean() || sType.isNumber()) && ! sType.isNullable())
- o = sType.getPrimitiveDefault();
- }
-
- if (o != null && ! (sType.isMapOrBean()))
- out.append('>');
-
- if (cr && ! (sType.isMapOrBean()))
- out.nl(indent+1);
- }
-
- ContentResult rc = CR_ELEMENTS;
-
- // Render the tag contents.
- if (o != null) {
- if (sType.isUri() || (pMeta != null && pMeta.isUri())) {
- out.textUri(o);
- } else if (sType.isCharSequence() || sType.isChar()) {
- if (format == XMLTEXT)
- out.append(o);
- else
- out.text(o, preserveWhitespace);
- } else if (sType.isNumber() || sType.isBoolean()) {
- out.append(o);
- } else if (sType.isMap() || (wType != null && wType.isMap())) {
- if (o instanceof BeanMap)
- rc = serializeBeanMap(session, out, (BeanMap)o, elementNamespace, isCollapsed, isMixed);
- else
- rc = serializeMap(session, out, (Map)o, sType, eType.getKeyType(), eType.getValueType(), isMixed);
- } else if (sType.isBean()) {
- rc = serializeBeanMap(session, out, session.toBeanMap(o), elementNamespace, isCollapsed, isMixed);
- } else if (sType.isCollection() || (wType != null && wType.isCollection())) {
- if (isCollapsed)
- session.indent--;
- serializeCollection(session, out, o, sType, eType, pMeta, isMixed);
- if (isCollapsed)
- session.indent++;
- } else if (sType.isArray()) {
- if (isCollapsed)
- session.indent--;
- serializeCollection(session, out, o, sType, eType, pMeta, isMixed);
- if (isCollapsed)
- session.indent++;
- } else {
- if (format == XMLTEXT)
- out.append(session.toString(o));
- else
- out.text(session.toString(o));
- }
- }
-
- session.pop();
-
- // Render the end tag.
- if (! isCollapsed) {
- if (rc == CR_EMPTY) {
- if (session.isHtmlMode())
- out.append('>').eTag(elementNs, en, encodeEn);
- else
- out.append('/').append('>');
- } else if (rc == CR_VOID || o == null) {
- out.append('/').append('>');
- }
- else
- out.ie(cr && rc != CR_MIXED ? indent : 0).eTag(elementNs, en, encodeEn);
- if (! isMixed)
- out.nl(indent);
- }
-
- return out;
- }
-
- private ContentResult serializeMap(XmlSerializerSession session, XmlWriter out, Map m, ClassMeta<?> sType,
- ClassMeta<?> eKeyType, ClassMeta<?> eValueType, boolean isMixed) throws Exception {
-
- m = session.sort(m);
-
- ClassMeta<?> keyType = eKeyType == null ? sType.getKeyType() : eKeyType;
- ClassMeta<?> valueType = eValueType == null ? sType.getValueType() : eValueType;
-
- boolean hasChildren = false;
- for (Iterator i = m.entrySet().iterator(); i.hasNext();) {
- Map.Entry e = (Map.Entry)i.next();
-
- Object k = e.getKey();
- if (k == null) {
- k = "\u0000";
- } else {
- k = session.generalize(k, keyType);
- if (session.isTrimStrings() && k instanceof String)
- k = k.toString().trim();
- }
-
- Object value = e.getValue();
-
- if (! hasChildren) {
- hasChildren = true;
- out.append('>').nlIf(! isMixed, session.getIndent());
- }
- serializeAnything(session, out, value, valueType, session.toString(k), null, false, XmlFormat.DEFAULT, isMixed, false, null);
- }
- return hasChildren ? CR_ELEMENTS : CR_EMPTY;
- }
-
- private ContentResult serializeBeanMap(XmlSerializerSession session, XmlWriter out, BeanMap<?> m,
- Namespace elementNs, boolean isCollapsed, boolean isMixed) throws Exception {
- boolean hasChildren = false;
- BeanMeta<?> bm = m.getMeta();
-
- List<BeanPropertyValue> lp = m.getValues(session.isTrimNulls());
-
- XmlBeanMeta xbm = bm.getExtendedMeta(XmlBeanMeta.class);
-
- Set<String>
- attrs = xbm.getAttrPropertyNames(),
- elements = xbm.getElementPropertyNames(),
- collapsedElements = xbm.getCollapsedPropertyNames();
- String
- attrsProperty = xbm.getAttrsPropertyName(),
- contentProperty = xbm.getContentPropertyName();
-
- XmlFormat cf = null;
-
- Object content = null;
- ClassMeta<?> contentType = null;
- for (BeanPropertyValue p : lp) {
- String n = p.getName();
- if (attrs.contains(n) || attrs.contains("*") || n.equals(attrsProperty)) {
- BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
-
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- session.onBeanGetterException(pMeta, t);
-
- if (session.canIgnoreValue(cMeta, key, value))
- continue;
-
- Namespace ns = (session.isEnableNamespaces() && pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() != elementNs ? pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace() : null);
-
- if (pMeta.isUri() ) {
- out.attrUri(ns, key, value);
- } else if (n.equals(attrsProperty)) {
- if (value instanceof BeanMap) {
- BeanMap<?> bm2 = (BeanMap)value;
- for (BeanPropertyValue p2 : bm2.getValues(true)) {
- String key2 = p2.getName();
- Object value2 = p2.getValue();
- Throwable t2 = p2.getThrown();
- if (t2 != null)
- session.onBeanGetterException(pMeta, t);
- out.attr(ns, key2, value2);
- }
- } else /* Map */ {
- Map m2 = (Map)value;
- for (Map.Entry e : (Set<Map.Entry>)(m2.entrySet())) {
- out.attr(ns, session.toString(e.getKey()), e.getValue());
- }
- }
- } else {
- out.attr(ns, key, value);
- }
- }
- }
-
- boolean
- hasContent = false,
- preserveWhitespace = false,
- isVoidElement = xbm.getContentFormat() == VOID;
-
- for (BeanPropertyValue p : lp) {
- BeanPropertyMeta pMeta = p.getMeta();
- ClassMeta<?> cMeta = p.getClassMeta();
-
- String n = p.getName();
- if (n.equals(contentProperty)) {
- content = p.getValue();
- contentType = p.getClassMeta();
- hasContent = true;
- cf = xbm.getContentFormat();
- if (cf.isOneOf(MIXED,MIXED_PWS,TEXT,TEXT_PWS,XMLTEXT))
- isMixed = true;
- if (cf.isOneOf(MIXED_PWS, TEXT_PWS))
- preserveWhitespace = true;
- if (contentType.isCollection() && ((Collection)content).isEmpty())
- hasContent = false;
- else if (contentType.isArray() && Array.getLength(content) == 0)
- hasContent = false;
- } else if (elements.contains(n) || collapsedElements.contains(n) || elements.contains("*") || collapsedElements.contains("*") ) {
- String key = p.getName();
- Object value = p.getValue();
- Throwable t = p.getThrown();
- if (t != null)
- session.onBeanGetterException(pMeta, t);
-
- if (session.canIgnoreValue(cMeta, key, value))
- continue;
-
- if (! hasChildren) {
- hasChildren = true;
- out.appendIf(! isCollapsed, '>').nlIf(! isMixed, session.getIndent());
- }
-
- XmlBeanPropertyMeta xbpm = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- serializeAnything(session, out, value, cMeta, key, xbpm.getNamespace(), false, xbpm.getXmlFormat(), isMixed, false, pMeta);
- }
- }
- if (! hasContent)
- return (hasChildren ? CR_ELEMENTS : isVoidElement ? CR_VOID : CR_EMPTY);
- out.append('>').nlIf(! isMixed, session.getIndent());
-
- // Serialize XML content.
- if (content != null) {
- if (contentType == null) {
- } else if (contentType.isCollection()) {
- Collection c = (Collection)content;
- for (Iterator i = c.iterator(); i.hasNext();) {
- Object value = i.next();
- serializeAnything(session, out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null);
- }
- } else if (contentType.isArray()) {
- Collection c = toList(Object[].class, content);
- for (Iterator i = c.iterator(); i.hasNext();) {
- Object value = i.next();
- serializeAnything(session, out, value, contentType.getElementType(), null, null, false, cf, isMixed, preserveWhitespace, null);
- }
- } else {
- serializeAnything(session, out, content, contentType, null, null, false, cf, isMixed, preserveWhitespace, null);
- }
- } else {
- if (! session.isTrimNulls()) {
- if (! isMixed)
- out.i(session.indent);
- out.text(content);
- if (! isMixed)
- out.nl(session.indent);
- }
- }
- return isMixed ? CR_MIXED : CR_ELEMENTS;
- }
-
- private XmlWriter serializeCollection(XmlSerializerSession session, XmlWriter out, Object in, ClassMeta<?> sType,
- ClassMeta<?> eType, BeanPropertyMeta ppMeta, boolean isMixed) throws Exception {
-
- ClassMeta<?> seType = sType.getElementType();
- if (seType == null)
- seType = session.object();
- ClassMeta<?> eeType = eType.getElementType();
-
- Collection c = (sType.isCollection() ? (Collection)in : toList(sType.getInnerClass(), in));
-
- c = session.sort(c);
-
- String type2 = null;
- if (sType != eType)
- type2 = sType.getDictionaryName();
-
- String eName = type2;
- Namespace eNs = null;
-
- if (ppMeta != null) {
- XmlBeanPropertyMeta xbpm = ppMeta.getExtendedMeta(XmlBeanPropertyMeta.class);
- eName = xbpm.getChildName();
- eNs = xbpm.getNamespace();
- }
-
- for (Iterator i = c.iterator(); i.hasNext();) {
- Object value = i.next();
- serializeAnything(session, out, value, eeType, eName, eNs, false, XmlFormat.DEFAULT, isMixed, false, null);
- }
- return out;
- }
/**
* Returns the schema serializer based on the settings of this serializer.
@@ -801,57 +236,12 @@ public class XmlSerializer extends WriterSerializer {
*/
public XmlSerializer getSchemaSerializer() {
if (schemaSerializer == null)
- schemaSerializer = new XmlSchemaSerializer(propertyStore, getOverrideProperties());
+ schemaSerializer = new XmlSchemaSerializer(propertyStore);
return schemaSerializer;
}
- static enum JsonType {
- STRING("string"),BOOLEAN("boolean"),NUMBER("number"),ARRAY("array"),OBJECT("object"),NULL("null");
-
- private final String value;
- private JsonType(String value) {
- this.value = value;
- }
-
- @Override
- public String toString() {
- return value;
- }
-
- boolean isOneOf(JsonType...types) {
- for (JsonType type : types)
- if (type == this)
- return true;
- return false;
- }
- }
-
- /**
- * Identifies what the contents were of a serialized bean.
- */
- static enum ContentResult {
- CR_VOID, // No content...append "/>" to the start tag.
- CR_EMPTY, // No content...append "/>" to the start tag if XML, "/></end>" if HTML.
- CR_MIXED, // Mixed content...don't add whitespace.
- CR_ELEMENTS // Elements...use normal whitespace rules.
- }
-
-
- //--------------------------------------------------------------------------------
- // Entry point methods
- //--------------------------------------------------------------------------------
-
- @Override /* Serializer */
- protected void doSerialize(SerializerSession session, SerializerOutput out, Object o) throws Exception {
- XmlSerializerSession s = (XmlSerializerSession)session;
- if (s.isEnableNamespaces() && s.isAutoDetectNamespaces())
- findNsfMappings(s, o);
- serializeAnything(s, s.getXmlWriter(out), o, s.getExpectedRootType(o), null, null, s.isEnableNamespaces() && s.isAddNamespaceUrlsToRoot(), XmlFormat.DEFAULT, false, false, null);
- }
-
@Override /* Serializer */
- public XmlSerializerSession createSession(ObjectMap op, Method javaMethod, Locale locale,
- TimeZone timeZone, MediaType mediaType, UriContext uriContext) {
- return new XmlSerializerSession(ctx, op, javaMethod, locale, timeZone, mediaType, uriContext);
+ public WriterSerializerSession createSession(SerializerSessionArgs args) {
+ return new XmlSerializerSession(ctx, args);
}
}