http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java index 0047ab4..d0e11e2 100644 --- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParser.java @@ -69,7 +69,7 @@ public class UrlEncodingParser extends UonParser { eType = (ClassMeta<T>)object(); PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap(); ClassMeta<?> sType = eType.getSerializedClassMeta(); - BeanDictionary bd = session.getBeanDictionary(); + BeanRegistry breg = session.getBeanRegistry(); int c = r.peek(); if (c == '?') @@ -83,7 +83,7 @@ public class UrlEncodingParser extends UonParser { if (m.containsKey("_value")) o = m.get("_value"); else - o = bd.cast(m); + o = breg.cast(m); } else if (sType.isMap()) { Map m = (sType.canCreateNewInstance() ? (Map)sType.newInstance() : new ObjectMap(session)); o = parseIntoMap(session, r, m, sType.getKeyType(), sType.getValueType()); @@ -101,7 +101,7 @@ public class UrlEncodingParser extends UonParser { ClassMeta<Object> valueType = object(); parseIntoMap(session, r, m, string(), valueType); if (m.containsKey(session.getBeanTypePropertyName())) - o = bd.cast(m); + o = breg.cast(m); else if (m.containsKey("_value")) o = session.convertToType(m.get("_value"), sType); else if (sType.isCollection()) {
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java index f611d9a..5adee94 100644 --- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingParserSession.java @@ -68,7 +68,7 @@ public class UrlEncodingParserSession extends UonParserSession { */ public final boolean shouldUseExpandedParams(BeanPropertyMeta pMeta) { ClassMeta<?> cm = pMeta.getClassMeta(); - if (cm.isArray() || cm.isCollection()) { + if (cm.isCollectionOrArray()) { if (expandedParams) return true; if (pMeta.getBeanMeta().getClassMeta().getExtendedMeta(UrlEncodingClassMeta.class).isExpandedParams()) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java index c762e90..e239a5c 100644 --- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/UrlEncodingSerializerSession.java @@ -60,7 +60,7 @@ public class UrlEncodingSerializerSession extends UonSerializerSession { */ public final boolean shouldUseExpandedParams(BeanPropertyMeta pMeta) { ClassMeta<?> cm = pMeta.getClassMeta(); - if (cm.isArray() || cm.isCollection()) { + if (cm.isCollectionOrArray()) { if (expandedParams) return true; if (pMeta.getBeanMeta().getClassMeta().getExtendedMeta(UrlEncodingClassMeta.class).isExpandedParams()) @@ -79,7 +79,7 @@ public class UrlEncodingSerializerSession extends UonSerializerSession { if (value == null || ! expandedParams) return false; ClassMeta<?> cm = getClassMetaForObject(value).getSerializedClassMeta(); - if (cm.isArray() || cm.isCollection()) { + if (cm.isCollectionOrArray()) { if (expandedParams) return true; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html index 4261c89..650e4c4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html +++ b/juneau-core/src/main/java/org/apache/juneau/urlencoding/package.html @@ -113,7 +113,7 @@ <li>Non-serializable classes and properties with associated <code>PojoSwaps</code> that convert them to serializable forms. </ul> <p> - Refer to <a href='../package-summary.html#PojoCategories' class='doclink'>POJO Categories</a> for a complete definition of supported POJOs. + Refer to <a href='../../../../overview-summary.html#Core.PojoCategories' class='doclink'>POJO Categories</a> for a complete definition of supported POJOs. </p> <h6 class='topic'>Prerequisites</h6> <p> http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java b/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java index 1d062f4..4e0d2ec 100644 --- a/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java +++ b/juneau-core/src/main/java/org/apache/juneau/utils/PojoQuery.java @@ -224,7 +224,7 @@ public final class PojoQuery { if (input == null) return null; - if (! (type.isBean() || type.isMap())) + if (! type.isMapOrBean()) throw new RuntimeException("Cannot call filterMap() on class type " + type); Map m = (Map)replaceWithMutables(input); @@ -249,7 +249,7 @@ public final class PojoQuery { if (input == null) return null; - if (! (type.isArray() || type.isCollection())) + if (! type.isCollectionOrArray()) throw new RuntimeException("Cannot call filterCollection() on class type " + type); if (view == null) @@ -305,7 +305,7 @@ public final class PojoQuery { DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), session); for (BeanMapEntry e : (Set<BeanMapEntry>)bm.entrySet()) { ClassMeta ct1 = e.getMeta().getClassMeta(); - if (ct1.isArray() || ct1.isBean() || ct1.isCollection() || ct1.isMap() || ct1.isObject()) + if (ct1.isCollectionOrArray() || ct1.isMapOrBean() || ct1.isObject()) dbm.put(e.getKey(), replaceWithMutables(e.getValue())); else dbm.addKey(e.getKey()); @@ -317,7 +317,7 @@ public final class PojoQuery { DelegateBeanMap dbm = new DelegateBeanMap(bm.getBean(), session); for (BeanMapEntry e : (Set<BeanMapEntry>)bm.entrySet()) { ClassMeta ct1 = e.getMeta().getClassMeta(); - if (ct1.isArray() || ct1.isBean() || ct1.isCollection() || ct1.isMap() || ct1.isObject()) { + if (ct1.isCollectionOrArray() || ct1.isMapOrBean() || ct1.isObject()) { Object val = null; try { val = e.getValue(); http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java index 25d98c6..6ffbdf4 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanMeta.java @@ -12,6 +12,7 @@ // *************************************************************************************************************************** package org.apache.juneau.xml; +import static org.apache.juneau.xml.annotation.XmlFormat.*; import java.util.*; import org.apache.juneau.*; @@ -23,10 +24,12 @@ import org.apache.juneau.xml.annotation.*; public class XmlBeanMeta extends BeanMetaExtended { // XML related fields - private final Map<String,BeanPropertyMeta> xmlAttrs; // Map of bean properties that are represented as XML attributes. - private final BeanPropertyMeta xmlContent; // Bean property that is represented as XML content within the bean element. - private final XmlContentHandler<?> xmlContentHandler; // Class used to convert bean to XML content. - private final Map<String,BeanPropertyMeta> childElementProperties; // Properties defined with @Xml.childName annotation. + private final Map<String,BeanPropertyMeta> attrs; // Map of bean properties that are represented as XML attributes. + private final Map<String,BeanPropertyMeta> elements; // Map of bean properties that are represented as XML elements. + private final BeanPropertyMeta attrsProperty; // Bean property that contain XML attribute key/value pairs for this bean. + private final Map<String,BeanPropertyMeta> collapsedProperties; // Properties defined with @Xml.childName annotation. + private BeanPropertyMeta contentProperty; + private XmlFormat contentFormat; /** * Constructor. @@ -36,73 +39,192 @@ public class XmlBeanMeta extends BeanMetaExtended { public XmlBeanMeta(BeanMeta<?> beanMeta) { super(beanMeta); Class<?> c = beanMeta.getClassMeta().getInnerClass(); + Xml xml = c.getAnnotation(Xml.class); + XmlFormat defaultFormat = null; + XmlFormat _contentFormat = null; - Map<String,BeanPropertyMeta> tXmlAttrs = new LinkedHashMap<String,BeanPropertyMeta>(); - BeanPropertyMeta tXmlContent = null; - XmlContentHandler<?> tXmlContentHandler = null; - Map<String,BeanPropertyMeta> tChildElementProperties = new LinkedHashMap<String,BeanPropertyMeta>(); + if (xml != null) { + XmlFormat xf = xml.format(); + if (xf == ATTRS) + defaultFormat = XmlFormat.ATTR; + else if (xf.isOneOf(ELEMENTS, DEFAULT)) + defaultFormat = ELEMENT; + else + throw new BeanRuntimeException(c, "Invalid format specified in @Xml annotation on bean: {0}. Must be one of the following: DEFAULT,ATTRS,ELEMENTS", xml.format()); + } + + Map<String,BeanPropertyMeta> _attrs = new LinkedHashMap<String,BeanPropertyMeta>(); + Map<String,BeanPropertyMeta> _elements = new LinkedHashMap<String,BeanPropertyMeta>(); + BeanPropertyMeta _attrsProperty = null, _contentProperty = null; + Map<String,BeanPropertyMeta> _collapsedProperties = new LinkedHashMap<String,BeanPropertyMeta>(); for (BeanPropertyMeta p : beanMeta.getPropertyMetas()) { XmlFormat xf = p.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(); - if (xf == XmlFormat.ATTR) - tXmlAttrs.put(p.getName(), p); - else if (xf == XmlFormat.CONTENT) { - if (tXmlContent != null) - throw new BeanRuntimeException(c, "Multiple instances of CONTENT properties defined on class. Only one property can be designated as such."); - tXmlContent = p; - tXmlContentHandler = p.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlContentHandler(); + ClassMeta<?> pcm = p.getClassMeta(); + if (xf == ATTR) { + _attrs.put(p.getName(), p); + } else if (xf == ELEMENT) { + _elements.put(p.getName(), p); + } else if (xf == COLLAPSED) { + _collapsedProperties.put(p.getName(), p); + } else if (xf == DEFAULT) { + if (defaultFormat == ATTR) + _attrs.put(p.getName(), p); + else + _elements.put(p.getName(), p); + } else if (xf == ATTRS) { + if (_attrsProperty != null) + throw new BeanRuntimeException(c, "Multiple instances of ATTRS properties defined on class. Only one property can be designated as such."); + if (! pcm.isMapOrBean()) + throw new BeanRuntimeException(c, "Invalid type for ATTRS property. Only properties of type Map and bean can be used."); + _attrsProperty = p; + } else if (xf.isOneOf(ELEMENTS, MIXED, TEXT, XMLTEXT)) { + if (xf.isOneOf(ELEMENTS, MIXED) && ! pcm.isCollectionOrArray()) + throw new BeanRuntimeException(c, "Invalid type for {0} property. Only properties of type Collection and array can be used.", xf); + if (_contentProperty != null) { + if (xf == _contentFormat) + throw new BeanRuntimeException(c, "Multiple instances of {0} properties defined on class. Only one property can be designated as such.", xf); + throw new BeanRuntimeException(c, "{0} and {1} properties found on the same bean. Only one property can be designated as such.", _contentFormat, xf); + } + _contentProperty = p; + _contentFormat = xf; } // Look for any properties that are collections with @Xml.childName specified. String n = p.getExtendedMeta(XmlBeanPropertyMeta.class).getChildName(); if (n != null) { - if (tChildElementProperties.containsKey(n)) - throw new BeanRuntimeException(c, "Multiple properties found with the name ''{0}''.", n); - tChildElementProperties.put(n, p); + if (_collapsedProperties.containsKey(n) && _collapsedProperties.get(n) != p) + throw new BeanRuntimeException(c, "Multiple properties found with the child name ''{0}''.", n); + _collapsedProperties.put(n, p); } } - xmlAttrs = Collections.unmodifiableMap(tXmlAttrs); - xmlContent = tXmlContent; - xmlContentHandler = tXmlContentHandler; - childElementProperties = (tChildElementProperties.isEmpty() ? null : Collections.unmodifiableMap(tChildElementProperties)); + attrs = Collections.unmodifiableMap(_attrs); + elements = Collections.unmodifiableMap(_elements); + attrsProperty = _attrsProperty; + collapsedProperties = Collections.unmodifiableMap(_collapsedProperties); + contentProperty = _contentProperty; + contentFormat = _contentFormat; + + // Do some validation. + if (contentProperty != null) { + if (! elements.isEmpty()) + throw new BeanRuntimeException(c, "{0} and ELEMENT properties found on the same bean. These cannot be mixed.", contentFormat); + if (! collapsedProperties.isEmpty()) + throw new BeanRuntimeException(c, "{0} and COLLAPSED properties found on the same bean. These cannot be mixed.", contentFormat); + } + + if (! collapsedProperties.isEmpty()) { + if (! Collections.disjoint(elements.keySet(), collapsedProperties.keySet())) + throw new BeanRuntimeException(c, "Child element name conflicts found with another property."); + } + } + + /** + * The list of properties that should be rendered as XML attributes. + * + * @return Map of property names to property metadata. + */ + protected Map<String,BeanPropertyMeta> getAttrProperties() { + return attrs; } /** - * Returns the list of properties annotated with an {@link Xml#format()} of {@link XmlFormat#ATTR}. - * In other words, the list of properties that should be rendered as XML attributes instead of child elements. + * The list of names of properties that should be rendered as XML attributes. * - * @return Metadata on the XML attribute properties of the bean. + * @return Set of property names. */ - protected Map<String,BeanPropertyMeta> getXmlAttrProperties() { - return xmlAttrs; + protected Set<String> getAttrPropertyNames() { + return attrs.keySet(); } /** - * Returns the bean property annotated with an {@link Xml#format()} value of {@link XmlFormat#CONTENT} + * The list of properties that should be rendered as child elements. * - * @return The bean property, or <jk>null</jk> if annotation is not specified. + * @return Map of property names to property metadata. */ - protected BeanPropertyMeta getXmlContentProperty() { - return xmlContent; + protected Map<String,BeanPropertyMeta> getElementProperties() { + return elements; } /** - * Return the XML content handler for this bean. + * The list of names of properties that should be rendered as child elements. * - * @return The XML content handler for this bean, or <jk>null</jk> if no content handler is defined. + * @return Set of property names. */ - protected XmlContentHandler<?> getXmlContentHandler() { - return xmlContentHandler; + protected Set<String> getElementPropertyNames() { + return elements.keySet(); } /** - * Returns the child element properties for this bean. + * The list of properties that should be rendered as collapsed child elements. * See {@link Xml#childName()} * - * @return The child element properties for this bean, or <jk>null</jk> if no child element properties are defined. + * @return Map of property names to property metadata. + */ + protected Map<String,BeanPropertyMeta> getCollapsedProperties() { + return collapsedProperties; + } + + /** + * The list of names of properties that should be rendered as collapsed child elements. + * + * @return Set of property names. + */ + protected Set<String> getCollapsedPropertyNames() { + return collapsedProperties.keySet(); + } + + /** + * The property that returns a map of XML attributes as key/value pairs. + * + * @return The bean property metadata, or <jk>null</jk> if there is no such method. + */ + protected BeanPropertyMeta getAttrsProperty() { + return attrsProperty; + } + + /** + * The name of the property that returns a map of XML attributes as key/value pairs. + * + * @return The bean property name, or <jk>null</jk> if there is no such method. + */ + protected String getAttrsPropertyName() { + return attrsProperty == null ? null : attrsProperty.getName(); + } + + /** + * The property that represents the inner XML content of this bean. + * + * @return The bean property metadata, or <jk>null</jk> if there is no such method. + */ + protected BeanPropertyMeta getContentProperty() { + return contentProperty; + } + + /** + * The name of the property that represents the inner XML content of this bean. + * + * @return The bean property name, or <jk>null</jk> if there is no such method. + */ + protected String getContentPropertyName() { + return contentProperty == null ? null : contentProperty.getName(); + } + + /** + * Returns the format of the inner XML content of this bean. + * <p> + * Can be one of the following: + * <ul> + * <li>{@link XmlFormat#ELEMENTS} + * <li>{@link XmlFormat#MIXED} + * <li>{@link XmlFormat#TEXT} + * <li>{@link XmlFormat#XMLTEXT} + * <li><jk>null</jk> + * + * @return The format of the inner XML content of this bean. */ - protected Map<String,BeanPropertyMeta> getChildElementProperties() { - return childElementProperties; + protected XmlFormat getContentFormat() { + return contentFormat; } /** @@ -114,8 +236,8 @@ public class XmlBeanMeta extends BeanMetaExtended { * @return The property metadata. */ protected BeanPropertyMeta getPropertyMeta(String fieldName) { - if (childElementProperties != null) { - BeanPropertyMeta bpm = childElementProperties.get(fieldName); + if (collapsedProperties != null) { + BeanPropertyMeta bpm = collapsedProperties.get(fieldName); if (bpm != null) return bpm; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java index 89ce61e..86333c0 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlBeanPropertyMeta.java @@ -23,8 +23,7 @@ import org.apache.juneau.xml.annotation.*; public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended { private Namespace namespace = null; - private XmlFormat xmlFormat = XmlFormat.NORMAL; - private XmlContentHandler<?> xmlContentHandler = null; + private XmlFormat xmlFormat = XmlFormat.DEFAULT; private String childName; /** @@ -72,22 +71,13 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended { /** * Returns the XML format of this property from the {@link Xml#format} annotation on this bean property. * - * @return The XML format, or {@link XmlFormat#NORMAL} if annotation not specified. + * @return The XML format, or {@link XmlFormat#DEFAULT} if annotation not specified. */ protected XmlFormat getXmlFormat() { return xmlFormat; } /** - * Returns the XML content handler of this property from the {@link Xml#contentHandler} annotation on this bean property. - * - * @return The XML content handler, or <jk>null</jk> if annotation not specified. - */ - protected XmlContentHandler<?> getXmlContentHandler() { - return xmlContentHandler; - } - - /** * Returns the child element of this property from the {@link Xml#childName} annotation on this bean property. * * @return The child element, or <jk>null</jk> if annotation not specified. @@ -108,10 +98,10 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended { List<XmlSchema> schemas = bpm.findAnnotations(XmlSchema.class); namespace = XmlUtils.findNamespace(xmls, schemas); - if (xmlFormat == XmlFormat.NORMAL) + if (xmlFormat == XmlFormat.DEFAULT) xmlFormat = xml.format(); - boolean isCollection = cmProperty.isCollection() || cmProperty.isArray(); + boolean isCollection = cmProperty.isCollectionOrArray(); String cen = xml.childName(); if ((! cen.isEmpty()) && (! isCollection)) @@ -132,13 +122,6 @@ public class XmlBeanPropertyMeta extends BeanPropertyMetaExtended { cen = cmProperty.getDictionaryName(); } - try { - if (xmlFormat == XmlFormat.CONTENT && xml.contentHandler() != XmlContentHandler.NULL.class) - xmlContentHandler = xml.contentHandler().newInstance(); - } catch (Exception e) { - throw new BeanRuntimeException(cmBean.getInnerClass(), "Could not instantiate content handler ''{0}''", xml.contentHandler().getName()).initCause(e); - } - if (! cen.isEmpty()) childName = cen; } http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlClassMeta.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlClassMeta.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlClassMeta.java index b23ef57..931f379 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlClassMeta.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlClassMeta.java @@ -43,7 +43,7 @@ public class XmlClassMeta extends ClassMetaExtended { this.childName = StringUtils.nullIfEmpty(xml.childName()); } else { - this.format = XmlFormat.NORMAL; + this.format = XmlFormat.DEFAULT; this.childName = null; } } @@ -60,7 +60,7 @@ public class XmlClassMeta extends ClassMetaExtended { /** * Returns the {@link Xml#format()} annotation defined on the class. * - * @return The value of the {@link Xml#format()} annotation, or {@link XmlFormat#NORMAL} if not specified. + * @return The value of the {@link Xml#format()} annotation, or {@link XmlFormat#DEFAULT} if not specified. */ protected XmlFormat getFormat() { return format; http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlContentHandler.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlContentHandler.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlContentHandler.java deleted file mode 100644 index 5521eaf..0000000 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlContentHandler.java +++ /dev/null @@ -1,137 +0,0 @@ -// *************************************************************************************************************************** -// * 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 javax.xml.stream.*; - -import org.apache.juneau.dto.atom.*; -import org.apache.juneau.xml.annotation.*; - -/** - * Customization class that allows a bean (or parts of a bean) to be serialized as XML text or mixed content. - * <p> - * For example, the ATOM specification allows text elements (e.g. title, subtitle...) - * to be either plain text or XML depending on the value of a <xa>type</xa> attribute. - * The behavior of text escaping thus depends on that attribute. - * - * <p class='bcode'> - * <xt><feed</xt> <xa>xmlns</xa>=<xs>"http://www.w3.org/2005/Atom"</xs><xt>></xt> - * <xt><title</xt> <xa>type</xa>=<xs>"html"</xs><xt>></xt> - * &lt;p&gt;&lt;i&gt;This is the title&lt;/i&gt;&lt;/p&gt; - * <xt></title></xt> - * <xt><title</xt> <xa>type</xa>=<xs>"xhtml"</xs><xt>></xt> - * <xt><div</xt> <xa>xmlns</xa>=<xs>"http://www.w3.org/1999/xhtml"</xs><xt>></xt> - * <xt><p><i></xt>This is the subtitle<xt></i></p></xt> - * <xt></div></xt> - * <xt></title></xt> - * <xt></feed></xt> - * </p> - * - * <p> - * The ATOM {@link Text} class (the implementation for both the <xt><title></xt> and <xt><subtitle></xt> - * tags shown above) then associates a content handler through the {@link Xml#contentHandler()} annotation - * on the bean property containing the text, like so... - * - * <p class='bcode'> - * <ja>@Xml</ja>(format=<jsf>ATTR</jsf>) - * <jk>public</jk> String getType() { - * <jk>return</jk> <jf>type</jf>; - * } - * - * <ja>@Xml</ja>(format=<jsf>CONTENT</jsf>, contentHandler=TextContentHandler.<jk>class</jk>) - * <jk>public</jk> String getText() { - * <jk>return</jk> <jf>text</jf>; - * } - * - * <jk>public void</jk> setText(String text) { - * <jk>this</jk>.<jf>text</jf> = text; - * } - * </p> - * - * <p> - * The content handler that transforms the output is shown below... - * - * <p class='bcode'> - * <jk>public static class</jk> TextContentHandler <jk>implements</jk> XmlContentHandler<Text> { - * - * <ja>@Override</ja> - * <jk>public void</jk> parse(XMLStreamReader r, Text text) <jk>throws</jk> Exception { - * String type = text.<jf>type</jf>; - * <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) - * text.<jf>text</jf> = <jsm>decode</jsm>(readXmlContents(r).trim()); - * <jk>else</jk> - * text.<jf>text</jf> = <jsm>decode</jsm>(r.getElementText().trim()); - * } - * - * <ja>@Override</ja> - * <jk>public void</jk> serialize(XmlSerializerWriter w, Text text) <jk>throws</jk> Exception { - * String type = text.<jf>type</jf>; - * String content = text.<jf>text</jf>; - * <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) - * w.encodeTextInvalidChars(content); - * <jk>else</jk> - * w.encodeText(content); - * } - * } - * </p> - * - * <h6 class='topic'>Notes</h6> - * <ul class='spaced-list'> - * <li>The {@link Xml#contentHandler()} annotation can only be specified on a bean class, or a bean property - * of format {@link XmlFormat#CONTENT}. - * </ul> - * - * @param <T> The class type of the bean - */ -public interface XmlContentHandler<T> { - - /** - * Represents <jk>null</jk> on the {@link Xml#contentHandler()} annotation. - */ - public static interface NULL extends XmlContentHandler<Object> {} - - /** - * Reads XML element content the specified reader and sets the appropriate value on the specified bean. - * <p> - * When this method is called, the attributes have already been parsed and set on the bean. - * Therefore, if the content handling is different based on some XML attribute (e.g. - * <code><xa>type</xa>=<xs>"text/xml"</xs></code> vs <code><xa>type</xa>=<xs>"text/plain"</xs></code>) - * then that attribute value can be obtained via the set bean property. - * - * @param r The XML stream reader. - * When called, the reader is positioned on the element containing the text to read. - * For example, calling <code>r.getElementText()</code> can be called immediately - * to return the element text if the element contains only characters and whitespace. - * However typically, the stream is going to contain XML elements that need to - * be handled special (otherwise you wouldn't need to use an <code>XmlContentHandler</code> - * to begin with). - * @param bean The bean where the parsed contents are going to be placed. - * Subclasses determine how the content maps to values in the bean. - * However, typically the contents map to a single property on the bean. - * @throws Exception If any problem occurs. Causes parse to fail. - */ - public void parse(XMLStreamReader r, T bean) throws Exception; - - /** - * Writes XML element content from values in the specified bean. - * - * @param w The XML output writer. - * When called, the XML element/attributes and - * whitespace/indentation (if enabled) have already been written to the stream. - * Subclasses must simply write the contents of the element. - * @param bean The bean whose values will be converted to XML content. - * @throws Exception If any problems occur. Causes serialize to fail. - */ - public void serialize(XmlWriter w, T bean) throws Exception; - -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java index a1f5b7f..85af340 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParser.java @@ -23,6 +23,7 @@ import javax.xml.stream.*; import org.apache.juneau.*; import org.apache.juneau.annotation.*; +import org.apache.juneau.internal.*; import org.apache.juneau.parser.*; import org.apache.juneau.transform.*; import org.apache.juneau.xml.annotation.*; @@ -55,15 +56,27 @@ public class XmlParser extends ReaderParser { private static final int UNKNOWN=0, OBJECT=1, ARRAY=2, STRING=3, NUMBER=4, BOOLEAN=5, NULL=6; - - private <T> T parseAnything(XmlParserSession session, ClassMeta<T> eType, String currAttr, XMLStreamReader r, Object outer, boolean isRoot, BeanPropertyMeta pMeta) throws Exception { + /** + * Workhorse method. + * + * @param session The current parser session. + * @param eType The expected type of object. + * @param currAttr The current bean property name. + * @param r The reader. + * @param outer The outer object. + * @param isRoot If <jk>true</jk>, then we're serializing a root element in the document. + * @param pMeta The bean property metadata. + * @return The parsed object. + * @throws Exception + */ + protected <T> T parseAnything(XmlParserSession session, ClassMeta<T> eType, String currAttr, XMLStreamReader r, Object outer, boolean isRoot, BeanPropertyMeta pMeta) throws Exception { if (eType == null) eType = (ClassMeta<T>)object(); PojoSwap<T,Object> transform = (PojoSwap<T,Object>)eType.getPojoSwap(); ClassMeta<?> sType = eType.getSerializedClassMeta(); session.setCurrentClass(sType); - BeanDictionary bd = (pMeta == null ? session.getBeanDictionary() : pMeta.getBeanDictionary()); + BeanRegistry breg = (pMeta == null ? session.getBeanRegistry() : pMeta.getBeanRegistry()); String wrapperAttr = (isRoot && session.isPreserveRootElement()) ? r.getName().getLocalPart() : null; String typeAttr = r.getAttributeValue(null, session.getBeanTypePropertyName()); @@ -81,13 +94,13 @@ public class XmlParser extends ReaderParser { jsonType = getJsonType(elementName); } } - if (! sType.canCreateNewInstance(outer)) { - if (bd.hasName(typeAttr)) { - sType = eType = (ClassMeta<T>)bd.getClassMeta(typeAttr); - } else if (bd.hasName(elementName)) { - sType = eType = (ClassMeta<T>)bd.getClassMeta(elementName); - } + + if (breg.hasName(typeAttr)) { + sType = eType = (ClassMeta<T>)breg.getClassMeta(typeAttr); + } else if (breg.hasName(elementName)) { + sType = eType = (ClassMeta<T>)breg.getClassMeta(elementName); } + Object o = null; if (jsonType == NULL) { @@ -108,26 +121,27 @@ public class XmlParser extends ReaderParser { parseIntoMap(session, r, m, string(), object(), pMeta); if (wrapperAttr != null) m = new ObjectMap(session).append(wrapperAttr, m); - o = bd.cast(m); + o = breg.cast(m); } else if (jsonType == ARRAY) o = parseIntoCollection(session, r, new ObjectList(session), object(), pMeta); else if (jsonType == STRING) { - o = session.decodeString(r.getElementText()); + o = session.decodeString(r); if (sType.isChar()) o = o.toString().charAt(0); } else if (jsonType == NUMBER) - o = parseNumber(session.decodeLiteral(r.getElementText()), null); + o = parseNumber(session.decodeText(r), null); else if (jsonType == BOOLEAN) - o = Boolean.parseBoolean(session.decodeLiteral(r.getElementText())); + o = Boolean.parseBoolean(session.decodeText(r)); else if (jsonType == UNKNOWN) o = getUnknown(session, r); } else if (sType.isBoolean()) { - o = Boolean.parseBoolean(session.decodeLiteral(r.getElementText())); + o = Boolean.parseBoolean(session.decodeText(r)); } else if (sType.isCharSequence()) { - o = session.decodeString(r.getElementText()); + o = session.decodeString(r); } else if (sType.isChar()) { - o = session.decodeString(r.getElementText()).charAt(0); + String s = session.decodeString(r); + o = s.length() == 0 ? 0 : s.charAt(0); } else if (sType.isMap()) { Map m = (sType.canCreateNewInstance(outer) ? (Map)sType.newInstance(outer) : new ObjectMap(session)); o = parseIntoMap(session, r, m, sType.getKeyType(), sType.getValueType(), pMeta); @@ -137,13 +151,13 @@ public class XmlParser extends ReaderParser { Collection l = (sType.canCreateNewInstance(outer) ? (Collection)sType.newInstance(outer) : new ObjectList(session)); o = parseIntoCollection(session, r, l, sType.getElementType(), pMeta); } else if (sType.isNumber()) { - o = parseNumber(session.decodeLiteral(r.getElementText()), (Class<? extends Number>)sType.getInnerClass()); + o = parseNumber(session.decodeText(r), (Class<? extends Number>)sType.getInnerClass()); } else if (sType.canCreateNewInstanceFromObjectMap(outer)) { ObjectMap m = new ObjectMap(session); parseIntoMap(session, r, m, string(), object(), pMeta); o = sType.newInstanceFromObjectMap(outer, m); } else if (sType.canCreateNewBean(outer)) { - if (sType.getExtendedMeta(XmlClassMeta.class).getFormat() == XmlFormat.COLLAPSED) { + if (sType.getExtendedMeta(XmlClassMeta.class).getFormat() == COLLAPSED) { String fieldName = r.getLocalName(); BeanMap<?> m = session.newBeanMap(outer, sType.getInnerClass()); BeanPropertyMeta bpm = m.getMeta().getExtendedMeta(XmlBeanMeta.class).getPropertyMeta(fieldName); @@ -160,11 +174,11 @@ public class XmlParser extends ReaderParser { ArrayList l = (ArrayList)parseIntoCollection(session, r, new ArrayList(), sType.getElementType(), pMeta); o = session.toArray(sType, l); } else if (sType.canCreateNewInstanceFromString(outer)) { - o = sType.newInstanceFromString(outer, session.decodeString(r.getElementText())); + o = sType.newInstanceFromString(outer, session.decodeString(r)); } else if (sType.canCreateNewInstanceFromNumber(outer)) { - o = sType.newInstanceFromNumber(session, outer, parseNumber(session.decodeLiteral(r.getElementText()), sType.getNewInstanceFromNumberClass())); + o = sType.newInstanceFromNumber(session, outer, parseNumber(session.decodeText(r), sType.getNewInstanceFromNumberClass())); } else { - throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", sType.getInnerClass().getName(), sType.getNotABeanReason()); + throw new ParseException(session, "Class ''{0}'' could not be instantiated. Reason: ''{1}'', property: ''{2}''", sType.getInnerClass().getName(), sType.getNotABeanReason(), pMeta == null ? null : pMeta.getName()); } if (transform != null && o != null) @@ -278,7 +292,9 @@ public class XmlParser extends ReaderParser { String val = r.getAttributeValue(i); BeanPropertyMeta bpm = xmlMeta.getPropertyMeta(key); if (bpm == null) { - if (m.getMeta().isSubTyped()) { + if (xmlMeta.getAttrsProperty() != null) { + xmlMeta.getAttrsProperty().add(m, key, val); + } else if (m.getMeta().isSubTyped()) { m.put(key, val); } else { Location l = r.getLocation(); @@ -289,59 +305,90 @@ public class XmlParser extends ReaderParser { } } - BeanPropertyMeta cp = xmlMeta.getXmlContentProperty(); - if (cp != null) { - XmlContentHandler h = xmlMeta.getXmlContentHandler(); - if (h != null) { - h.parse(r, m.getBean()); - } else { - String text = r.getElementText(); - cp.set(m, session.decodeString(text)); - } - return m; - } + BeanPropertyMeta cp = xmlMeta.getContentProperty(); + XmlFormat cpf = xmlMeta.getContentFormat(); + ClassMeta<?> cpcm = (cp == null ? session.object() : cp.getClassMeta()); + StringBuilder sb = (cpf != null && cpf.isOneOf(TEXT,XMLTEXT) ? session.getStringBuilder() : null); int depth = 0; do { - int event = r.nextTag(); + int event = r.next(); String currAttr; - if (event == START_ELEMENT) { - depth++; - currAttr = session.decodeString(r.getLocalName()); - BeanPropertyMeta pMeta = xmlMeta.getPropertyMeta(currAttr); - if (pMeta == null) { - if (m.getMeta().isSubTyped()) { - Object value = parseAnything(session, string(), currAttr, r, m.getBean(false), false, null); - m.put(currAttr, value); - } else { - Location l = r.getLocation(); - onUnknownProperty(session, currAttr, m, l.getLineNumber(), l.getColumnNumber()); - skipCurrentTag(r); - } + // We only care about text in MIXED mode. + // Ignore if in ELEMENTS mode. + if (event == CHARACTERS) { + if (cpf == MIXED && cp != null) { + if (cpcm.isCollectionOrArray()) + cp.add(m, session.decodeString(r.getText())); + else + cp.set(m, session.decodeString(r.getText())); + } else if (sb != null) { + String s = r.getText(); + if (! StringUtils.isEmpty(s)) + sb.append(s); } else { - session.setCurrentProperty(pMeta); - XmlFormat xf = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(); - if (xf == COLLAPSED) { - ClassMeta<?> et = pMeta.getClassMeta().getElementType(); - Object value = parseAnything(session, et, currAttr, r, m.getBean(false), false, pMeta); - setName(et, value, currAttr); - pMeta.add(m, value); - } else if (xf == ATTR) { - pMeta.set(m, session.decodeString(r.getAttributeValue(0))); - r.nextTag(); + // Do nothing...we're in ELEMENTS mode. + } + } else if (event == START_ELEMENT) { + if (cpf == TEXT) { + throw new ParseException("Element found where simple text was expected. {0}", XmlUtils.toReadableEvent(r)); + } else if (cpf == XMLTEXT && sb != null) { + sb.append(session.elementAsString(r)); + depth++; + } else if (cpf == MIXED && cp != null) { + if (cpcm.isCollectionOrArray()) + cp.add(m, parseAnything(session, cpcm.getElementType(), cp.getName(), r, m.getBean(false), false, cp)); + else + cp.set(m, parseAnything(session, cpcm, cp.getName(), r, m.getBean(false), false, cp)); + } else if (cpf == ELEMENTS && cp != null) { + cp.add(m, parseAnything(session, cpcm.getElementType(), cp.getName(), r, m.getBean(false), false, cp)); + } else { + currAttr = session.decodeString(r.getLocalName()); + BeanPropertyMeta pMeta = xmlMeta.getPropertyMeta(currAttr); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + Object value = parseAnything(session, string(), currAttr, r, m.getBean(false), false, null); + m.put(currAttr, value); + } else { + Location l = r.getLocation(); + onUnknownProperty(session, currAttr, m, l.getLineNumber(), l.getColumnNumber()); + skipCurrentTag(r); + } } else { - ClassMeta<?> cm = pMeta.getClassMeta(); - Object value = parseAnything(session, cm, currAttr, r, m.getBean(false), false, pMeta); - setName(cm, value, currAttr); - pMeta.set(m, value); + session.setCurrentProperty(pMeta); + XmlFormat xf = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(); + if (xf == COLLAPSED) { + ClassMeta<?> et = pMeta.getClassMeta().getElementType(); + Object value = parseAnything(session, et, currAttr, r, m.getBean(false), false, pMeta); + setName(et, value, currAttr); + pMeta.add(m, value); + } else if (xf == ATTR) { + pMeta.set(m, session.decodeString(r.getAttributeValue(0))); + r.nextTag(); + } else { + ClassMeta<?> cm = pMeta.getClassMeta(); + Object value = parseAnything(session, cm, currAttr, r, m.getBean(false), false, pMeta); + setName(cm, value, currAttr); + pMeta.set(m, value); + } + session.setCurrentProperty(null); } - session.setCurrentProperty(null); } } else if (event == END_ELEMENT) { + if (depth > 0) { + if (cpf == XMLTEXT && sb != null) + sb.append(session.elementAsString(r)); + else + throw new ParseException("End element found where one was not expected. {0}", XmlUtils.toReadableEvent(r)); + } depth--; - return m; + } else { + throw new ParseException("Unexpected event type: {0}", event); } - } while (depth > 0); + } while (depth >= 0); + if (sb != null && cp != null) + cp.set(m, session.decodeString(sb.toString())); + session.returnStringBuilder(sb); return m; } @@ -373,7 +420,7 @@ public class XmlParser extends ReaderParser { } } int eventType = r.next(); - StringBuilder sb = new StringBuilder(); + StringBuilder sb = session.getStringBuilder(); while (eventType != XMLStreamConstants.END_ELEMENT) { if (eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA || eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.ENTITY_REFERENCE) { sb.append(r.getText()); @@ -418,6 +465,7 @@ public class XmlParser extends ReaderParser { eventType = r.next(); } String s = sb.toString(); + session.returnStringBuilder(sb); s = session.decodeString(s); if (m != null) { if (! s.isEmpty()) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserContext.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserContext.java index 3af7487..7b4ae26 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserContext.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserContext.java @@ -62,20 +62,6 @@ import org.apache.juneau.parser.*; * <td><jk>true</jk></td> * </tr> * <tr> - * <td>{@link #XML_coalescing}</td> - * <td>Enable text element coalescing.</td> - * <td><code>Boolean<code></td> - * <td><jk>false</jk></td> - * <td><jk>true</jk></td> - * </tr> - * <tr> - * <td>{@link #XML_replaceEntityReferences}</td> - * <td>Replace entity references.</td> - * <td><code>Boolean<code></td> - * <td><jk>true</jk></td> - * <td><jk>true</jk></td> - * </tr> - * <tr> * <td>{@link #XML_reporter}</td> * <td>XML reporter.</td> * <td>{@link XMLReporter}</td> @@ -113,7 +99,7 @@ import org.apache.juneau.parser.*; * </ul> * </ul> */ -public final class XmlParserContext extends ParserContext { +public class XmlParserContext extends ParserContext { /** * <b>Configuration property:</b> XMLSchema-instance namespace URI. @@ -159,36 +145,6 @@ public final class XmlParserContext extends ParserContext { public static final String XML_validating = "XmlParser.validating"; /** - * <b>Configuration property:</b> Enable text element coalescing. - * <p> - * <ul> - * <li><b>Name:</b> <js>"XmlParser.coalescing"</js> - * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>false</jk> - * <li><b>Session-overridable:</b> <jk>true</jk> - * </ul> - * <p> - * If <jk>true</jk>, XML text elements will be coalesced. - * See {@link XMLInputFactory#IS_COALESCING} for more info. - */ - public static final String XML_coalescing = "XmlParser.coalescing"; - - /** - * <b>Configuration property:</b> Replace entity references. - * <p> - * <ul> - * <li><b>Name:</b> <js>"XmlParser.replaceEntityReferences"</js> - * <li><b>Data type:</b> <code>Boolean</code> - * <li><b>Default:</b> <jk>true</jk> - * <li><b>Session-overridable:</b> <jk>true</jk> - * </ul> - * <p> - * If <jk>true</jk>, entity references will be replace during parsing. - * See {@link XMLInputFactory#IS_REPLACING_ENTITY_REFERENCES} for more info. - */ - public static final String XML_replaceEntityReferences = "XmlParser.replaceEntityReferences"; - - /** * <b>Configuration property:</b> XML reporter. * <p> * <ul> @@ -265,8 +221,6 @@ public final class XmlParserContext extends ParserContext { final boolean trimWhitespace, validating, - coalescing, - replaceEntityReferences, preserveRootElement; final XMLReporter reporter; final XMLResolver resolver; @@ -284,8 +238,6 @@ public final class XmlParserContext extends ParserContext { xsiNs = cf.getProperty(XML_xsiNs, String.class, "http://www.w3.org/2001/XMLSchema-instance"); trimWhitespace = cf.getProperty(XML_trimWhitespace, boolean.class, true); validating = cf.getProperty(XML_validating, boolean.class, false); - coalescing = cf.getProperty(XML_coalescing, boolean.class, false); - replaceEntityReferences = cf.getProperty(XML_replaceEntityReferences, boolean.class, true); preserveRootElement = cf.getProperty(XML_preserveRootElement, boolean.class, false); reporter = cf.getProperty(XML_reporter, XMLReporter.class, null); resolver = cf.getProperty(XML_resolver, XMLResolver.class, null); @@ -299,8 +251,6 @@ public final class XmlParserContext extends ParserContext { .append("xsiNs", xsiNs) .append("trimWhitespace", trimWhitespace) .append("validating", validating) - .append("coalescing", coalescing) - .append("replaceEntityReferences", replaceEntityReferences) .append("preserveRootElement", preserveRootElement) .append("reporter", reporter) .append("resolver", resolver) http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserSession.java index 5eadec4..5aeb93d 100644 --- a/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserSession.java +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlParserSession.java @@ -24,25 +24,25 @@ import javax.xml.stream.util.*; import org.apache.juneau.*; import org.apache.juneau.internal.*; import org.apache.juneau.parser.*; +import org.apache.juneau.xml.annotation.*; /** * Session object that lives for the duration of a single use of {@link XmlParser}. * <p> * This class is NOT thread safe. It is meant to be discarded after one-time use. */ -public final class XmlParserSession extends ParserSession { +public class XmlParserSession extends ParserSession { private final String xsiNs; private final boolean trimWhitespace, validating, - coalescing, - replaceEntityReferences, preserveRootElement; private final XMLReporter reporter; private final XMLResolver resolver; private final XMLEventAllocator eventAllocator; private XMLStreamReader xmlStreamReader; + private final StringBuilder sb = new StringBuilder(); // Reusable string builder used in this class. /** * Create a new session using properties specified in the context. @@ -72,8 +72,6 @@ public final class XmlParserSession extends ParserSession { xsiNs = ctx.xsiNs; trimWhitespace = ctx.trimWhitespace; validating = ctx.validating; - coalescing = ctx.coalescing; - replaceEntityReferences = ctx.replaceEntityReferences; reporter = ctx.reporter; resolver = ctx.resolver; eventAllocator = ctx.eventAllocator; @@ -82,8 +80,6 @@ public final class XmlParserSession extends ParserSession { xsiNs = op.getString(XML_xsiNs, ctx.xsiNs); trimWhitespace = op.getBoolean(XML_trimWhitespace, ctx.trimWhitespace); validating = op.getBoolean(XML_validating, ctx.validating); - coalescing = op.getBoolean(XML_coalescing, ctx.coalescing); - replaceEntityReferences = op.getBoolean(XML_replaceEntityReferences, ctx.replaceEntityReferences); reporter = (XMLReporter)op.get(XML_reporter, ctx.reporter); resolver = (XMLResolver)op.get(XML_resolver, ctx.resolver); eventAllocator = (XMLEventAllocator)op.get(XML_eventAllocator, ctx.eventAllocator); @@ -120,8 +116,8 @@ public final class XmlParserSession extends ParserSession { Reader r = IOUtils.getBufferedReader(getReader()); XMLInputFactory factory = XMLInputFactory.newInstance(); factory.setProperty(XMLInputFactory.IS_VALIDATING, validating); - factory.setProperty(XMLInputFactory.IS_COALESCING, coalescing); - factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, replaceEntityReferences); + factory.setProperty(XMLInputFactory.IS_COALESCING, true); + factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true); if (factory.isPropertySupported(XMLInputFactory.REPORTER) && reporter != null) factory.setProperty(XMLInputFactory.REPORTER, reporter); if (factory.isPropertySupported(XMLInputFactory.RESOLVER) && resolver != null) @@ -152,13 +148,25 @@ public final class XmlParserSession extends ParserSession { return s; if (trimWhitespace) s = s.trim(); - s = XmlUtils.decode(s); + sb.setLength(0); + s = XmlUtils.decode(s, sb); if (isTrimStrings()) s = s.trim(); return s; } /** + * Shortcut for calling <code>decodeString(r.getElementText());</code>. + * + * @param r The reader to read the element text from. + * @return The decoded text. + * @throws XMLStreamException + */ + public final String decodeString(XMLStreamReader r) throws XMLStreamException { + return decodeString(r.getElementText()); + } + + /** * Decodes the specified literal (e.g. <js>"true"</js>, <js>"123"</js>). * <p> * Unlike <code>decodeString(String)</code>, the input string is ALWAYS trimmed before decoding, and @@ -171,11 +179,45 @@ public final class XmlParserSession extends ParserSession { if (s == null || s.isEmpty()) return s; s = s.trim(); - s = XmlUtils.decode(s); + sb.setLength(0); + s = XmlUtils.decode(s, sb); return s; } /** + * Shortcut for calling <code>decodeLiteral(r.getElementText());</code>. + * + * @param r The reader to read the element text from. + * @return The decoded text. + * @throws XMLStreamException + */ + public final String decodeText(XMLStreamReader r) throws XMLStreamException { + return decodeLiteral(r.getElementText()); + } + + /** + * Takes the element being read from the XML stream reader and reconstructs it as XML. + * <p> + * Used when reconstructing bean properties of type {@link XmlFormat#XMLTEXT}. + * + * @param r The XML stream reader to read the current event from. + * @return The event as XML. + * @throws RuntimeException if the event is not a start or end tag. + */ + public final String elementAsString(XMLStreamReader r) { + int t = r.getEventType(); + if (t > 2) + throw new RuntimeException("Invalid event type on stream reader for elementToString() method: " + XmlUtils.toReadableEvent(r)); + sb.setLength(0); + sb.append("<").append(t == 1 ? "" : "/").append(r.getLocalName()); + if (t == 1) + for (int i = 0; i < r.getAttributeCount(); i++) + sb.append(' ').append(r.getAttributeName(i)).append('=').append('\'').append(r.getAttributeValue(i)).append('\''); + sb.append('>'); + return sb.toString(); + } + + /** * Silently closes the XML stream. */ @Override /* ParserContext */ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/4fb01038/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 a91ad29..2e107a5 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 @@ -328,6 +328,7 @@ public class XmlSchemaSerializer extends XmlSerializer { 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); @@ -335,27 +336,13 @@ public class XmlSchemaSerializer extends XmlSerializer { // 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 ((cm.isBean() && cm.getBeanMeta().getExtendedMeta(XmlBeanMeta.class).getXmlContentProperty() != null) || cm.isObject()) + if ((xbm != null && (xbm.getContentFormat() != null && xbm.getContentFormat().isOneOf(TEXT,MIXED,XMLTEXT))) || ! cm.isMapOrBean()) w.attr("mixed", "true"); w.cTag().nl(); - if (! (cm.isMap() || cm.isBean() || cm.hasToObjectMapMethod() || cm.isCollection() || cm.isArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) { - String base = getXmlAttrType(cm); - w.sTag(i+1, "simpleContent").nl(); - w.oTag(i+2, "extension") - .attr("base", base); - if (session.isAddJsonTypeAttrs() || (session.isAddJsonStringTypeAttrs() && base.equals("string"))) { - w.cTag().nl(); - w.oTag(i+3, "attribute") - .attr("name", session.getBeanTypePropertyName()) - .attr("type", "string") - .ceTag().nl(); - w.eTag(i+2, "extension").nl(); - } else { - w.ceTag().nl(); - } - w.eTag(i+1, "simpleContent").nl(); + if (! (cm.isMapOrBean() || cm.hasToObjectMapMethod() || cm.isCollectionOrArray() || (cm.isAbstract() && ! cm.isNumber()) || cm.isObject())) { + w.oTag(i+1, "attribute").attr("name", session.getBeanTypePropertyName()).attr("type", "string").ceTag().nl(); } else { @@ -365,11 +352,14 @@ public class XmlSchemaSerializer extends XmlSerializer { boolean hasChildElements = false; - for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) - if (pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat() != XmlFormat.ATTR && pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat() != XmlFormat.CONTENT) + for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) { + XmlFormat pMetaFormat = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getXmlFormat(); + if (pMetaFormat != XmlFormat.ATTR) hasChildElements = true; + } - if (bm.getExtendedMeta(XmlBeanMeta.class).getXmlContentProperty() != null) { + XmlBeanMeta xbm2 = bm.getExtendedMeta(XmlBeanMeta.class); + if (xbm2.getContentProperty() != null && xbm2.getContentFormat() == ELEMENTS) { w.sTag(i+1, "sequence").nl(); w.oTag(i+2, "any") .attr("processContents", "skip") @@ -378,57 +368,70 @@ public class XmlSchemaSerializer extends XmlSerializer { w.eTag(i+1, "sequence").nl(); } else if (hasChildElements) { - w.sTag(i+1, "sequence").nl(); boolean hasOtherNsElement = false; + boolean hasCollapsed = false; for (BeanPropertyMeta pMeta : bm.getPropertyMetas()) { XmlBeanPropertyMeta xmlMeta = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class); - if (xmlMeta.getXmlFormat() != XmlFormat.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), true) - .attr("type", getXmlType(cNs, ct2)); - if (isCollapsed) { - w.attr("minOccurs", 0); - w.attr("maxOccurs", "unbounded"); - } else { - if (! session.isTrimNulls()) - w.attr("nillable", true); - else - w.attr("minOccurs", 0); - } - - w.ceTag().nl(); - } else { + 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 this bean has any child elements in another namespace, - // we need to add an <any> element. - if (hasOtherNsElement) + 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(); w.oTag(i+2, "any") + .attr("processContents", "skip") .attr("minOccurs", 0) - .attr("maxOccurs", "unbounded") .ceTag().nl(); - w.eTag(i+1, "sequence").nl(); + w.eTag(i+1, "choice").nl(); + + } else { + w.sTag(i+1, "all").nl(); + 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), true) + .attr("type", getXmlType(cNs, ct2)) + .attr("minOccurs", 0); + + w.ceTag().nl(); + } else { + // Child element is in another namespace. + schemas.queueElement(cNs, pMeta.getName(), ct2); + hasOtherNsElement = true; + } + + } + } + w.eTag(i+1, "all").nl(); + } + } - for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getXmlAttrProperties().values()) { + for (BeanPropertyMeta pMeta : bm.getExtendedMeta(XmlBeanMeta.class).getAttrProperties().values()) { Namespace pNs = pMeta.getExtendedMeta(XmlBeanPropertyMeta.class).getNamespace(); if (pNs == null) pNs = defaultNs; @@ -453,7 +456,7 @@ public class XmlSchemaSerializer extends XmlSerializer { } //----- Collection ----- - } else if (cm.isCollection() || cm.isArray()) { + } else if (cm.isCollectionOrArray()) { ClassMeta<?> elementType = cm.getElementType(); if (elementType.isObject()) { w.sTag(i+1, "sequence").nl(); @@ -486,12 +489,10 @@ public class XmlSchemaSerializer extends XmlSerializer { w.eTag(i+1, "sequence").nl(); } - if (session.isAddBeanTypeProperties() || session.isAddJsonTypeAttrs()) { - w.oTag(i+1, "attribute") - .attr("name", session.getBeanTypePropertyName()) - .attr("type", "string") - .ceTag().nl(); - } + w.oTag(i+1, "attribute") + .attr("name", session.getBeanTypePropertyName()) + .attr("type", "string") + .ceTag().nl(); } w.eTag(i, "complexType").nl(); @@ -509,9 +510,9 @@ public class XmlSchemaSerializer extends XmlSerializer { name = "boolean"; else if (cm.isNumber()) name = "number"; - else if (cm.isArray() || cm.isCollection()) + else if (cm.isCollectionOrArray()) name = "array"; - else if (! (cm.isMap() || cm.hasToObjectMapMethod() || cm.isBean() || cm.isCollection() || cm.isArray() || cm.isObject() || cm.isAbstract())) + else if (! (cm.isMapOrBean() || cm.hasToObjectMapMethod() || cm.isCollectionOrArray() || cm.isObject() || cm.isAbstract())) name = "string"; else name = "object"; @@ -532,18 +533,16 @@ public class XmlSchemaSerializer extends XmlSerializer { private String getXmlType(Namespace currentNs, ClassMeta<?> cm) { String name = null; cm = cm.getSerializedClassMeta(); - if (currentNs == targetNs && ! session.isAddJsonTypeAttrs()) { - if (cm.isBoolean()) - name = "boolean"; - else if (cm.isNumber()) { - if (cm.isDecimal()) - name = "decimal"; - else - name = "integer"; - } - if (name == null && ! session.isAddJsonStringTypeAttrs()) { - if (! (cm.isMap() || cm.hasToObjectMapMethod() || cm.isBean() || cm.isCollection() || cm.isArray() || cm.isObject() || cm.isAbstract())) - name = "string"; + 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) {