http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java new file mode 100644 index 0000000..c7a90aa --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerContext.java @@ -0,0 +1,161 @@ +/*************************************************************************************************************************** + * 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.*; +import org.apache.juneau.serializer.*; + +/** + * Configurable properties on the {@link XmlSerializer} class. + * <p> + * Context properties are set by calling {@link ContextFactory#setProperty(String, Object)} on the context factory + * returned {@link CoreApi#getContextFactory()}. + * <p> + * The following convenience methods are also provided for setting context properties: + * <ul> + * <li>{@link XmlSerializer#setProperty(String,Object)} + * <li>{@link XmlSerializer#setProperties(ObjectMap)} + * <li>{@link XmlSerializer#addNotBeanClasses(Class[])} + * <li>{@link XmlSerializer#addTransforms(Class[])} + * <li>{@link XmlSerializer#addImplClass(Class,Class)} + * </ul> + * <p> + * See {@link ContextFactory} for more information about context properties. + * + * @author James Bognar ([email protected]) + */ +public class XmlSerializerContext extends SerializerContext { + + /** + * Add JSON type attributes to output ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <js>true</jk>, {@code type} attributes will be added to elements in the XML for number/boolean/null nodes. + */ + public static final String XML_addJsonTypeAttrs = "XmlSerializer.addJsonTypeAttrs"; + + /** + * Add JSON type attributes for strings to output ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, {@code type} attributes will be added to elements in the XML for string nodes. + * <p> + * By default, these attributes are not added, and the parser will assume that the content type + * of the node is string by default. + * <p> + * This feature is disabled if {@link #XML_addJsonTypeAttrs} is disabled. + */ + public static final String XML_addJsonStringTypeAttrs = "XmlSerializer.addJsonStringTypeAttrs"; + + /** + * Enable support for XML namespaces ({@link Boolean}, default=<jk>true</jk>). + * <p> + * If not enabled, XML output will not contain any namespaces regardless of any other settings. + */ + public static final String XML_enableNamespaces = "XmlSerializer.enableNamespaces"; + + /** + * Auto-detect namespace usage ({@link Boolean}, default=<jk>true</jk>). + * <p> + * Detect namespace usage before serialization. + * <p> + * Used in conjunction with {@link #XML_addNamespaceUrisToRoot} to reduce + * the list of namespace URLs appended to the root element to only those + * that will be used in the resulting document. + * <p> + * If enabled, then the data structure will first be crawled looking for + * namespaces that will be encountered before the root element is + * serialized. + * <p> + * This setting is ignored if {@link #XML_enableNamespaces} is not enabled. + * <p> + * <b>IMPORTANT NOTE:</b> + * Auto-detection of namespaces can be costly performance-wise. + * In high-performance environments, it's recommended that namespace detection be + * disabled, and that namespaces be manually defined through the {@link #XML_namespaces} property. + */ + public static final String XML_autoDetectNamespaces = "XmlSerializer.autoDetectNamespaces"; + + /** + * Add namespace URLs to the root element ({@link Boolean}, default=<jk>true</jk>). + * <p> + * Use this setting to add {@code xmlns:x} attributes to the root + * element for the default and all mapped namespaces. + * <p> + * This setting is ignored if {@link #XML_enableNamespaces} is not enabled. + */ + public static final String XML_addNamespaceUrisToRoot = "XmlSerializer.addNamespaceUrisToRoot"; + + /** + * Default namespace URI ({@link String}, default=<jk>null</jk>). + * <p> + * Specifies the default namespace URI for this document. + */ + public static final String XML_defaultNamespaceUri = "XmlSerializer.defaultNamespaceUri"; + + /** + * XMLSchema namespace ({@link Namespace}, default=<code>{name:<js>'xs'</js>,uri:<js>'http://www.w3.org/2001/XMLSchema'</js>}</code>). + * <p> + * Specifies the namespace for the <code>XMLSchema</code> namespace, used by the schema generated + * by the {@link XmlSchemaSerializer} class. + */ + public static final String XML_xsNamespace = "XmlSerializer.xsNamespace"; + + /** + * XMLSchema-Instance namespace ({@link Namespace}, default=<code>{name:<js>'xsi'</js>,uri:<js>'http://www.w3.org/2001/XMLSchema-instance'</js>}</code>). + * <p> + * Specifies the namespace of the <code>XMLSchema-instance</code> namespace used for<code>nil=<jk>true</jk></code> attributes. + */ + public static final String XML_xsiNamespace = "XmlSerializer.xsiNamespace"; + + /** + * Default namespaces (<code>Set<Namespace></code>, default=empty set). + * <p> + * The default list of namespaces associated with this serializer. + */ + public static final String XML_namespaces = "XmlSerializer.namespaces"; + + + final boolean + addJsonTypeAttrs, + addJsonStringTypeAttrs, + autoDetectNamespaces, + enableNamespaces, + addNamespaceUrlsToRoot; + + final String defaultNamespace; + + final Namespace + xsiNamespace, + xsNamespace; + + final Namespace[] namespaces; + + /** + * Constructor. + * <p> + * Typically only called from {@link ContextFactory#getContext(Class)}. + * + * @param cf The factory that created this context. + */ + public XmlSerializerContext(ContextFactory cf) { + super(cf); + addJsonTypeAttrs = cf.getProperty(XML_addJsonTypeAttrs, boolean.class, false); + addJsonStringTypeAttrs = cf.getProperty(XML_addJsonStringTypeAttrs, boolean.class, false); + autoDetectNamespaces = cf.getProperty(XML_autoDetectNamespaces, boolean.class, true); + enableNamespaces = cf.getProperty(XML_enableNamespaces, boolean.class, true); + addNamespaceUrlsToRoot = cf.getProperty(XML_addNamespaceUrisToRoot, boolean.class, true); + defaultNamespace = cf.getProperty(XML_defaultNamespaceUri, String.class, "{juneau:'http://www.ibm.com/2013/Juneau'}"); + xsNamespace = cf.getProperty(XML_xsNamespace, Namespace.class, new Namespace("xs", "http://www.w3.org/2001/XMLSchema")); + xsiNamespace = cf.getProperty(XML_xsiNamespace, Namespace.class, new Namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance")); + namespaces = cf.getProperty(XML_namespaces, Namespace[].class, new Namespace[0]); + } +} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java new file mode 100644 index 0000000..9e67fb5 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlSerializerSession.java @@ -0,0 +1,208 @@ +/*************************************************************************************************************************** + * 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.xml.NamespaceFactory.*; +import static org.apache.juneau.xml.XmlSerializerContext.*; + +import java.lang.reflect.*; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; +import org.apache.juneau.json.*; +import org.apache.juneau.serializer.*; + +/** + * Session object that lives for the duration of a single use of {@link XmlSerializer}. + * <p> + * This class is NOT thread safe. It is meant to be discarded after one-time use. + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings("hiding") +public class XmlSerializerSession extends SerializerSession { + + private final boolean + addJsonTypeAttrs, + addJsonStringTypeAttrs, + autoDetectNamespaces, + enableNamespaces, + addNamespaceUrlsToRoot; + + private Namespace + defaultNamespace; + private final Namespace + xsiNamespace, + xsNamespace; + + private Namespace[] namespaces = new Namespace[0]; + + /** + * 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 beanContext The bean context being used. + * @param output The output object. See {@link JsonSerializerSession#getWriter()} for valid class types. + * @param op The override properties. + * These override any context properties defined in the context. + * @param javaMethod The java method that called this parser, usually the method in a REST servlet. + */ + public XmlSerializerSession(XmlSerializerContext ctx, BeanContext beanContext, Object output, ObjectMap op, Method javaMethod) { + super(ctx, beanContext, output, op, javaMethod); + if (op == null || op.isEmpty()) { + addJsonTypeAttrs = ctx.addJsonTypeAttrs; + addJsonStringTypeAttrs = ctx.addJsonStringTypeAttrs; + enableNamespaces = ctx.enableNamespaces; + autoDetectNamespaces = ctx.autoDetectNamespaces; + addNamespaceUrlsToRoot = ctx.addNamespaceUrlsToRoot; + addNamespaces(ctx.namespaces); + defaultNamespace = findDefaultNamespace(ctx.defaultNamespace); + xsiNamespace = ctx.xsiNamespace; + xsNamespace = ctx.xsNamespace; + } else { + addJsonTypeAttrs = op.getBoolean(XML_addJsonTypeAttrs, ctx.addJsonTypeAttrs); + addJsonStringTypeAttrs = op.getBoolean(XML_addJsonStringTypeAttrs, ctx.addJsonStringTypeAttrs); + enableNamespaces = op.getBoolean(XML_enableNamespaces, ctx.enableNamespaces); + autoDetectNamespaces = op.getBoolean(XML_autoDetectNamespaces, ctx.autoDetectNamespaces); + addNamespaceUrlsToRoot = op.getBoolean(XML_addNamespaceUrisToRoot, ctx.addNamespaceUrlsToRoot); + namespaces = (op.containsKey(XML_namespaces) ? parseNamespaces(op.get(XML_namespaces)) : ctx.namespaces); + defaultNamespace = findDefaultNamespace(op.containsKey(XML_defaultNamespaceUri) ? op.getString(XML_defaultNamespaceUri) : ctx.defaultNamespace); + xsiNamespace = (op.containsKey(XML_xsiNamespace) ? parseNamespace(op.get(XML_xsiNamespace)) : ctx.xsiNamespace); + xsNamespace = (op.containsKey(XML_xsNamespace) ? parseNamespace(op.get(XML_xsNamespace)) : ctx.xsNamespace); + } + } + + private Namespace findDefaultNamespace(String s) { + if (s == null) + return null; + if (StringUtils.startsWith(s, '{')) + return parseNamespace(s); + if (! s.startsWith("http://")) + return get(s, "http://unknown"); + return get(null, s); + } + + private void addNamespaces(Namespace...namespaces) { + for (Namespace ns : namespaces) + addNamespace(ns); + } + + /** + * Add a namespace to this session. + * + * @param ns The namespace being added. + */ + public void addNamespace(Namespace ns) { + if (ns == defaultNamespace) + return; + + for (Namespace n : namespaces) + if (n == ns) + return; + + if (defaultNamespace != null && (ns.uri.equals(defaultNamespace.uri) || ns.name.equals(defaultNamespace.name))) + defaultNamespace = ns; + else + namespaces = ArrayUtils.append(namespaces, ns); + } + + /** + * Returns the list of namespaces being used in the current XML serialization. + * + * @return The list of namespaces being used in the current XML serialization. + */ + public Namespace[] getNamespaces() { + return namespaces; + } + + /** + * Returns the {@link XmlSerializerContext#XML_addJsonTypeAttrs} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_addJsonTypeAttrs} setting value in this context. + */ + public final boolean isAddJsonTypeAttrs() { + return addJsonTypeAttrs; + } + + /** + * Returns the {@link XmlSerializerContext#XML_addJsonStringTypeAttrs} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_addJsonStringTypeAttrs} setting value in this context. + */ + public final boolean isAddJsonStringTypeAttrs() { + return addJsonStringTypeAttrs; + } + + /** + * Returns the {@link XmlSerializerContext#XML_autoDetectNamespaces} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_autoDetectNamespaces} setting value in this context. + */ + public final boolean isAutoDetectNamespaces() { + return enableNamespaces && autoDetectNamespaces; + } + + /** + * Returns the {@link XmlSerializerContext#XML_enableNamespaces} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_enableNamespaces} setting value in this context. + */ + public final boolean isEnableNamespaces() { + return enableNamespaces; + } + + /** + * Returns the {@link XmlSerializerContext#XML_addNamespaceUrisToRoot} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_addNamespaceUrisToRoot} setting value in this context. + */ + public final boolean isAddNamespaceUrlsToRoot() { + return addNamespaceUrlsToRoot; + } + + /** + * Returns the {@link XmlSerializerContext#XML_defaultNamespaceUri} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_defaultNamespaceUri} setting value in this context. + */ + public final Namespace getDefaultNamespace() { + return defaultNamespace; + } + + /** + * Returns the {@link XmlSerializerContext#XML_xsiNamespace} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_xsiNamespace} setting value in this context. + */ + public final Namespace getXsiNamespace() { + return xsiNamespace; + } + + /** + * Returns the {@link XmlSerializerContext#XML_xsNamespace} setting value in this context. + * + * @return The {@link XmlSerializerContext#XML_xsNamespace} setting value in this context. + */ + public final Namespace getXsNamespace() { + return xsNamespace; + } + + @Override /* SerializerSession */ + public XmlWriter getWriter() throws Exception { + Object output = getOutput(); + if (output instanceof XmlWriter) + return (XmlWriter)output; + return new XmlWriter(super.getWriter(), isUseIndentation(), isTrimStrings(), getQuoteChar(), getRelativeUriBase(), getAbsolutePathUriBase(), isEnableNamespaces(), getDefaultNamespace()); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java new file mode 100644 index 0000000..c418f5d --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlUtils.java @@ -0,0 +1,575 @@ +/*************************************************************************************************************************** + * 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 javax.xml.stream.XMLStreamConstants.*; + +import java.io.*; +import java.util.*; + +import javax.xml.namespace.*; +import javax.xml.stream.*; + +import org.apache.juneau.*; +import org.apache.juneau.internal.*; +import org.apache.juneau.xml.annotation.*; + +/** + * XML utility methods. + * + * @author James Bognar ([email protected]) + */ +public final class XmlUtils { + + //-------------------------------------------------------------------------------- + // Encode URI part + //-------------------------------------------------------------------------------- + + /** + * Encodes invalid XML text characters. + * <p> + * Encodes <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> as XML entities.<br> + * Encodes any other invalid XML text characters to <code>_x####_</code> sequences. + * + * @param o The object being encoded. + * @return The encoded string. + */ + public static final String encodeText(Object o) { + + if (o == null) + return "_x0000_"; + + String s = o.toString(); + + try { + if (needsTextEncoding(s)) + return encodeTextInner(new StringBuilderWriter(s.length()*2), s).toString(); + } catch (IOException e) { + throw new RuntimeException(e); // Never happens + } + + return s; + } + + /** + * Same as {@link #encodeText(Object)}, but does not convert <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> + * to entities. + * + * @param o The object being encoded. + * @return The encoded string. + */ + public static final String encodeTextInvalidChars(Object o) { + + if (o == null) + return "_x0000_"; + + String s = o.toString(); + + try { + if (needsTextEncoding(s)) + return encodeTextInvalidCharsInner(new StringBuilderWriter(s.length()*2), s).toString(); + } catch (IOException e) { + throw new RuntimeException(e); // Never happens + } + + return s; + } + + /** + * Encodes any invalid XML text characters to <code>_x####_</code> sequences and sends the response + * to the specified writer. + * + * @param w The writer to send the output to. + * @param o The object being encoded. + * @return The same writer passed in. + * @throws IOException Thrown from the writer. + */ + public static final Writer encodeText(Writer w, Object o) throws IOException { + + if (o == null) + return w.append("_x0000_"); + + String s = o.toString(); + + if (needsTextEncoding(s)) + return encodeTextInner(w, s); + + w.append(s); + + return w; + } + + /** + * Same as {@link #encodeText(Object)}, but does not convert <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> + * to entities. + * + * @param w The writer to write to. + * @param o The object being encoded. + * @return The encoded string. + * @throws IOException + */ + public static final Writer encodeTextInvalidChars(Writer w, Object o) throws IOException { + + if (o == null) + return w.append("_x0000_"); + + String s = o.toString(); + + if (needsTextEncoding(s)) + return encodeTextInvalidCharsInner(w, s); + + w.append(s); + + return w; + } + + /** + * Same as {@link #encodeText(Object)}, but only converts <js>'&'</js>, <js>'<'</js>, and <js>'>'</js> + * to entities. + * + * @param w The writer to write to. + * @param o The object being encoded. + * @return The encoded string. + * @throws IOException + */ + public static final Writer encodeTextXmlChars(Writer w, Object o) throws IOException { + if (o == null) + return w; + + String s = o.toString(); + + if (needsTextEncoding(s)) + return encodeTextXmlCharsInner(w, s); + + w.append(s); + + return w; + + } + + private static final Writer encodeTextInner(Writer w, String s) throws IOException { + final int len = s.length(); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if (c == '&') + w.append("&"); + else if (c == '<') + w.append("<"); + else if (c == '>') + w.append(">"); + else if (c == '_' && isEscapeSequence(s,i)) + appendPaddedHexChar(w, c); + else if ((i == 0 || i == len-1) && Character.isWhitespace(c)) + appendPaddedHexChar(w, c); + else if (isValidXmlCharacter(c)) + w.append(c); + else if (c == 0x09 || c == 0x0A || c == 0x0D) + w.append("�").append(Integer.toHexString(c)).append(";"); + else + appendPaddedHexChar(w, c); + } + return w; + } + + private static final Writer encodeTextInvalidCharsInner(Writer w, String s) throws IOException { + final int len = s.length(); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if ((i == 0 || i == len-1) && Character.isWhitespace(c)) + appendPaddedHexChar(w, c); + else if (c == '_' && isEscapeSequence(s,i)) + appendPaddedHexChar(w, c); + else if (isValidXmlCharacter(c)) + w.append(c); + else + appendPaddedHexChar(w, c); + } + return w; + } + + private static final Writer encodeTextXmlCharsInner(Writer w, String s) throws IOException { + final int len = s.length(); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if (c == '&') + w.append("&"); + else if (c == '<') + w.append("<"); + else if (c == '>') + w.append(">"); + else + w.append(c); + } + return w; + } + + private static final boolean needsTextEncoding(String s) { + // See if we need to convert the string. + // Conversion is somewhat expensive, so make sure we need to do so before hand. + final int len = s.length(); + for (int i = 0; i < len; i++) { + char c = s.charAt(i); + if ((i == 0 || i == len-1) && Character.isWhitespace(c)) + return true; + if (c == '&' || c == '<' || c == '>' || c == '\n' || ! isValidXmlCharacter(c) || (c == '_' && isEscapeSequence(s,i))) + return true; + } + return false; + } + + + //-------------------------------------------------------------------------------- + // Decode XML text + //-------------------------------------------------------------------------------- + + /** + * Translates any _x####_ sequences (introduced by the various encode methods) back into their original characters. + * + * @param s The string being decoded. + * @return The decoded string. + */ + public static final String decode(String s) { + if (s == null) return null; + if (s.length() == 0) + return s; + if (s.indexOf('_') == -1) + return s; + + StringBuffer sb = new StringBuffer(s.length()); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '_' && isEscapeSequence(s,i)) { + + int x = Integer.parseInt(s.substring(i+2, i+6), 16); + + // If we find _x0000_, then that means a null. + if (x == 0) + return null; + + sb.append((char)x); + i+=6; + } else { + sb.append(c); + } + } + return sb.toString(); + } + + + //-------------------------------------------------------------------------------- + // Encode XML attributes + //-------------------------------------------------------------------------------- + + /** + * Serializes and encodes the specified object as valid XML attribute name. + * + * @param w The writer to send the output to. + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public static final Writer encodeAttr(Writer w, Object o) throws IOException { + + if (o == null) + return w.append("_x0000_"); + + String s = o.toString(); + + if (needsAttributeEncoding(s)) + return encodeAttrInner(w, s); + + w.append(s); + return w; + } + + private static final Writer encodeAttrInner(Writer w, String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '&') + w.append("&"); + else if (c == '<') + w.append("<"); + else if (c == '>') + w.append(">"); + else if (c == '\'') + w.append("'"); + else if (c == '"') + w.append("""); + else if (c == '_' && isEscapeSequence(s,i)) + appendPaddedHexChar(w, c); + else if (isValidXmlCharacter(c)) + w.append(c); + else + appendPaddedHexChar(w, c); + } + return w; + } + + private static boolean needsAttributeEncoding(String s) { + // See if we need to convert the string. + // Conversion is somewhat expensive, so make sure we need to do so before hand. + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '&' || c == '<' || c == '>' || c == '\n' || c == '\'' || c == '"' || ! isValidXmlCharacter(c)) + return true; + } + return false; + } + + + //-------------------------------------------------------------------------------- + // Encode XML element names + //-------------------------------------------------------------------------------- + + /** + * Encodes any invalid XML element name characters to <code>_x####_</code> sequences. + * + * @param w The writer to send the output to. + * @param o The object being encoded. + * @return The same writer passed in. + * @throws IOException Throw by the writer. + */ + public static final Writer encodeElementName(Writer w, Object o) throws IOException { + + if (o == null) + return w.append("_x0000_"); + + String s = o.toString(); + + if (needsElementNameEncoding(s)) + return encodeElementNameInner(w, s); + + w.append(s); + return w; + } + + /** + * Encodes any invalid XML element name characters to <code>_x####_</code> sequences. + * + * @param o The object being encoded. + * @return The encoded element name string. + */ + public static final String encodeElementName(Object o) { + if (o == null) + return "_x0000_"; + + String s = o.toString(); + + try { + if (needsElementNameEncoding(s)) + return encodeElementNameInner(new StringBuilderWriter(s.length() * 2), s).toString(); + } catch (IOException e) { + throw new RuntimeException(e); // Never happens + } + + return s; + } + + private static final Writer encodeElementNameInner(Writer w, String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c >= 'A' && c <= 'Z') + || (c == '_' && ! isEscapeSequence(s,i)) + || (c >= 'a' && c <= 'z') + || (i != 0 && ( + c == '-' + || c == '.' + || (c >= '0' && c <= '9') + || c == '\u00b7' + || (c >= '\u0300' && c <= '\u036f') + || (c >= '\u203f' && c <= '\u2040') + )) + || (c >= '\u00c0' && c <= '\u00d6') + || (c >= '\u00d8' && c <= '\u00f6') + || (c >= '\u00f8' && c <= '\u02ff') + || (c >= '\u0370' && c <= '\u037d') + || (c >= '\u037f' && c <= '\u1fff') + || (c >= '\u200c' && c <= '\u200d') + || (c >= '\u2070' && c <= '\u218f') + || (c >= '\u2c00' && c <= '\u2fef') + || (c >= '\u3001' && c <= '\ud7ff') + || (c >= '\uf900' && c <= '\ufdcf') + || (c >= '\ufdf0' && c <= '\ufffd')) { + w.append(c); + } else { + appendPaddedHexChar(w, c); + } + } + return w; + } + + private static final boolean needsElementNameEncoding(String s) { + // Note that this doesn't need to be perfect, just fast. + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (! (c >= '0' && c <= '9' || c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z')) + return true; + if (i == 0 && (c >= '0' && c <= '9')) + return true; + } + return false; + } + + + //-------------------------------------------------------------------------------- + // Other methods + //-------------------------------------------------------------------------------- + + /** + * Utility method for reading XML mixed content from an XML element and returning it as text. + * + * @param r The reader to read from. + * @return The contents read as a string. + * @throws XMLStreamException + * @throws IOException + */ + public static String readXmlContents(XMLStreamReader r) throws XMLStreamException, IOException { + StringWriter sw = new StringWriter(); + XmlWriter w = new XmlWriter(sw, false, false, '"', null, null, false, null); + try { + int depth = 0; + do { + int event = r.next(); + if (event == START_ELEMENT) { + depth++; + QName n = r.getName(); + w.oTag(n.getPrefix(), n.getLocalPart()); + for (int i = 0; i < r.getNamespaceCount(); i++) + w.attr(r.getNamespacePrefix(i), "xmlns", r.getNamespaceURI(i)); + for (int i = 0; i < r.getAttributeCount(); i++) + w.attr(r.getAttributePrefix(i), r.getAttributeLocalName(i), r.getAttributeValue(i)); + w.append('>'); + } else if (r.hasText()) { + w.encodeTextXmlChars(r.getText()); + } else if (event == ATTRIBUTE) { + // attributes handled above. + } else if (event == END_ELEMENT) { + QName n = r.getName(); + if (depth > 0) + w.eTag(n.getPrefix(), n.getLocalPart()); + depth--; + } + if (depth < 0) + return sw.toString(); + } while (true); + } finally { + w.close(); + } + } + + // Returns true if the specified character can safely be used in XML text or an attribute. + private static final boolean isValidXmlCharacter(char c) { + return (c >= 0x20 && c <= 0xD7FF) /*|| c == 0xA || c == 0xD*/ || (c >= 0xE000 && c <= 0xFFFD); + } + + // Returns true if the string at the specified position is of the form "_x####_" + // where '#' are hexadecimal characters. + private static final boolean isEscapeSequence(String s, int i) { + return s.length() > i+6 + && s.charAt(i) == '_' + && s.charAt(i+1) == 'x' + && isHexCharacter(s.charAt(i+2)) + && isHexCharacter(s.charAt(i+3)) + && isHexCharacter(s.charAt(i+4)) + && isHexCharacter(s.charAt(i+5)) + && s.charAt(i+6) == '_'; + } + + // Returns true if the character is a hexadecimal character + private static final boolean isHexCharacter(char c) { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'); + } + + // Converts an integer to a hexadecimal string padded to 4 places. + private static final Writer appendPaddedHexChar(Writer out, int num) throws IOException { + out.append("_x"); + char[] n = new char[4]; + int a = num%16; + n[3] = (char)(a > 9 ? 'A'+a-10 : '0'+a); + int base = 16; + for (int i = 1; i < 4; i++) { + a = (num/base)%16; + base <<= 4; + n[3-i] = (char)(a > 9 ? 'A'+a-10 : '0'+a); + } + for (int i = 0; i < 4; i++) + out.append(n[i]); + return out.append('_'); + } + + /** + * Find the namespace given a list of <ja>@Xml</ja> and <ja>@XmlSchema</ja> annotations. + * The annotations should be a child-to-parent ordering of annotations found on + * a class or method. + * + * @param xmls The list of <ja>@Xml</ja> annotations. + * @param schemas The list of <ja>@XmlSchema</ja> annotations. + * @return The namespace, or <jk>null</jk> if it couldn't be found. + */ + public static Namespace findNamespace(List<Xml> xmls, List<XmlSchema> schemas) { + + for (Xml xml : xmls) { + Namespace ns = findNamespace(xml.prefix(), xml.namespace(), xmls, schemas); + if (ns != null) + return ns; + } + + for (XmlSchema schema : schemas) { + Namespace ns = findNamespace(schema.prefix(), schema.namespace(), null, schemas); + if (ns != null) + return ns; + } + + return null; + } + + private static Namespace findNamespace(String prefix, String ns, List<Xml> xmls, List<XmlSchema> schemas) { + + // If both prefix and namespace specified, use that Namespace mapping. + if (! (prefix.isEmpty() || ns.isEmpty())) + return NamespaceFactory.get(prefix, ns); + + // If only prefix specified, need to search for namespaceURI. + if (! prefix.isEmpty()) { + if (xmls != null) + for (Xml xml2 : xmls) + if (xml2.prefix().equals(prefix) && ! xml2.namespace().isEmpty()) + return NamespaceFactory.get(prefix, xml2.namespace()); + for (XmlSchema schema : schemas) { + if (schema.prefix().equals(prefix) && ! schema.namespace().isEmpty()) + return NamespaceFactory.get(prefix, schema.namespace()); + for (XmlNs xmlNs : schema.xmlNs()) + if (xmlNs.prefix().equals(prefix)) + return NamespaceFactory.get(prefix, xmlNs.namespaceURI()); + } + throw new BeanRuntimeException("Found @Xml.prefix annotation with no matching URI. prefix='"+prefix+"'"); + } + + // If only namespaceURI specified, need to search for prefix. + if (! ns.isEmpty()) { + if (xmls != null) + for (Xml xml2 : xmls) + if (xml2.namespace().equals(ns) && ! xml2.prefix().isEmpty()) + return NamespaceFactory.get(xml2.prefix(), ns); + for (XmlSchema schema : schemas) { + if (schema.namespace().equals(ns) && ! schema.prefix().isEmpty()) + return NamespaceFactory.get(schema.prefix(), ns); + for (XmlNs xmlNs : schema.xmlNs()) + if (xmlNs.namespaceURI().equals(ns)) + return NamespaceFactory.get(xmlNs.prefix(), ns); + } + } + + return null; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java new file mode 100644 index 0000000..3aee57f --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/XmlWriter.java @@ -0,0 +1,667 @@ +/*************************************************************************************************************************** + * 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 java.io.*; + +import org.apache.juneau.serializer.*; + +/** + * Specialized writer for serializing XML. + * <p> + * <b>Note: This class is not intended for external use.</b> + * + * @author James Bognar ([email protected]) + */ +public class XmlWriter extends SerializerWriter { + + private String defaultNsPrefix; + private boolean enableNs; + + /** + * Constructor. + * + * @param out The wrapped writer. + * @param useIndentation If <jk>true</jk> XML elements will be indented. + * @param trimStrings If <jk>true</jk>, strings should be trimmed before they're serialized. + * @param quoteChar The quote character to use for attributes. Should be <js>'\''</js> or <js>'"'</js>. + * @param relativeUriBase The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>). + * @param absolutePathUriBase The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>). + * @param enableNs Flag to indicate if XML namespaces are enabled. + * @param defaultNamespace The default namespace if XML namespaces are enabled. + */ + public XmlWriter(Writer out, boolean useIndentation, boolean trimStrings, char quoteChar, String relativeUriBase, String absolutePathUriBase, boolean enableNs, Namespace defaultNamespace) { + super(out, useIndentation, true, trimStrings, quoteChar, relativeUriBase, absolutePathUriBase); + this.enableNs = enableNs; + this.defaultNsPrefix = defaultNamespace == null ? null : defaultNamespace.name; + } + + /** + * Writes an opening tag to the output: <code><xt><ns:name</xt></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(String ns, String name, boolean needsEncoding) throws IOException { + append('<'); + if (enableNs && ns != null && ! (ns.isEmpty() || ns.equals(defaultNsPrefix))) + append(ns).append(':'); + if (needsEncoding) + encodeElement(name); + else + append(name); + return this; + } + + /** + * Shortcut for <code>oTag(ns, name, <jk>false</jk>);</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(String ns, String name) throws IOException { + return oTag(ns, name, false); + } + + /** + * Shortcut for <code>oTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(String name) throws IOException { + return oTag(null, name, false); + } + + /** + * Shortcut for <code>i(indent).oTag(ns, name, needsEncoding);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + return i(indent).oTag(ns, name, needsEncoding); + } + + /** + * Shortcut for <code>i(indent).oTag(ns, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(int indent, String ns, String name) throws IOException { + return i(indent).oTag(ns, name, false); + } + + /** + * Shortcut for <code>i(indent).oTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oTag(int indent, String name) throws IOException { + return i(indent).oTag(null, name, false); + } + + /** + * Closes a tag. + * Shortcut for <code>append(<js>'>'</js>);</code> + * + * @return This object (for method chaining). + * @throws IOException + */ + public XmlWriter cTag() throws IOException { + append('>'); + return this; + } + + /** + * Closes an empty tag. + * Shortcut for <code>append(<js>'/'</js>).append(<js>'>'</js>);</code> + * + * @return This object (for method chaining). + * @throws IOException + */ + public XmlWriter ceTag() throws IOException { + append('/').append('>'); + return this; + } + + /** + * Writes a closed tag to the output: <code><xt><ns:name/></xt></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(String ns, String name, boolean needsEncoding) throws IOException { + append('<'); + if (enableNs && ns != null && ! (ns.isEmpty() || ns.equals(defaultNsPrefix))) + append(ns).append(':'); + if (needsEncoding) + encodeElement(name); + else + append(name); + return append('/').append('>'); + } + + /** + * Shortcut for <code>tag(ns, name, <jk>false</jk>);</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(String ns, String name) throws IOException { + return tag(ns, name, false); + } + + /** + * Shortcut for <code>tag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(String name) throws IOException { + return tag(null, name, false); + } + + /** + * Shortcut for <code>i(indent).tag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(int indent, String name) throws IOException { + return i(indent).tag(name); + } + + /** + * Shortcut for <code>i(indent).tag(ns, name, needsEncoding);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + return i(indent).tag(ns, name, needsEncoding); + } + + /** + * Shortcut for <code>i(indent).tag(ns, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter tag(int indent, String ns, String name) throws IOException { + return i(indent).tag(ns, name); + } + + + /** + * Writes a start tag to the output: <code><xt><ns:name></xt></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(String ns, String name, boolean needsEncoding) throws IOException { + return oTag(ns, name, needsEncoding).append('>'); + } + + /** + * Shortcut for <code>sTag(ns, name, <jk>false</jk>);</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(String ns, String name) throws IOException { + return sTag(ns, name, false); + } + + /** + * Shortcut for <code>sTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(String name) throws IOException { + return sTag(null, name); + } + + /** + * Shortcut for <code>i(indent).sTag(ns, name, needsEncoding);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + return i(indent).sTag(ns, name, needsEncoding); + } + + /** + * Shortcut for <code>i(indent).sTag(ns, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(int indent, String ns, String name) throws IOException { + return i(indent).sTag(ns, name, false); + } + + /** + * Shortcut for <code>i(indent).sTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter sTag(int indent, String name) throws IOException { + return i(indent).sTag(null, name, false); + } + + + /** + * Writes an end tag to the output: <code><xt></ns:name></xt></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(String ns, String name, boolean needsEncoding) throws IOException { + append('<').append('/'); + if (enableNs && ns != null && ! (ns.isEmpty() || ns.equals(defaultNsPrefix))) + append(ns).append(':'); + if (needsEncoding) + encodeElement(name); + else + append(name); + return append('>'); + } + + /** + * Shortcut for <code>eTag(ns, name, <jk>false</jk>);</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(String ns, String name) throws IOException { + return eTag(ns, name, false); + } + + /** + * Shortcut for <code>eTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(String name) throws IOException { + return eTag(null, name); + } + + /** + * Shortcut for <code>i(indent).eTag(ns, name, needsEncoding);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @param needsEncoding If <jk>true</jk>, element name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(int indent, String ns, String name, boolean needsEncoding) throws IOException { + return i(indent).eTag(ns, name, needsEncoding); + } + + /** + * Shortcut for <code>i(indent).eTag(ns, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(int indent, String ns, String name) throws IOException { + return i(indent).eTag(ns, name, false); + } + + /** + * Shortcut for <code>i(indent).eTag(<jk>null</jk>, name, <jk>false</jk>);</code> + * + * @param indent The number of prefix tabs to add. + * @param name The element name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter eTag(int indent, String name) throws IOException { + return i(indent).eTag(name); + } + + /** + * Writes an attribute to the output: <code><xa>ns:name</xa>=<xs>'value'</xs></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @param value The attribute value. + * @param needsEncoding If <jk>true</jk>, attribute name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attr(String ns, String name, Object value, boolean needsEncoding) throws IOException { + oAttr(ns, name).q(); + if (needsEncoding) + encodeAttr(value); + else + append(value); + return q(); + } + + /** + * Shortcut for <code>attr(<jk>null</jk>, name, value, <jk>false</jk>);</code> + * + * @param name The attribute name. + * @param value The attribute value. + * @param needsEncoding If <jk>true</jk>, attribute name will be encoded. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attr(String name, Object value, boolean needsEncoding) throws IOException { + return attr(null, name, value, needsEncoding); + } + + /** + * Shortcut for <code>attr(ns, name, value, <jk>false</jk>);</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @param value The attribute value. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attr(String ns, String name, Object value) throws IOException { + return oAttr(ns, name).q().append(value).q(); + } + + /** + * Same as {@link #attr(String, Object, boolean)}, except pass in a {@link Namespace} object for the namespace. + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @param value The attribute value. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attr(Namespace ns, String name, Object value) throws IOException { + return oAttr(ns == null ? null : ns.name, name).q().append(value).q(); + } + + /** + * Shortcut for <code>attr(<jk>null</jk>, name, value, <jk>false</jk>);</code> + * + * @param name The attribute name. + * @param value The attribute value. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attr(String name, Object value) throws IOException { + return attr((String)null, name, value); + } + + + /** + * Writes an open-ended attribute to the output: <code><xa>ns:name</xa>=</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oAttr(String ns, String name) throws IOException { + append(' '); + if (enableNs && ns != null && ! (ns.isEmpty() || ns.equals(defaultNsPrefix))) + append(ns).append(':'); + append(name).append('='); + return this; + } + + /** + * Writes an open-ended attribute to the output: <code><xa>ns:name</xa>=</code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter oAttr(Namespace ns, String name) throws IOException { + return oAttr(ns == null ? null : ns.name, name); + } + + /** + * Writes an attribute with a URI value to the output: <code><xa>ns:name</xa>=<xs>'uri-value'</xs></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @param value The attribute value, convertable to a URI via <code>toString()</code> + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attrUri(Namespace ns, String name, Object value) throws IOException { + oAttr(ns, name).q().appendUri(value).q(); + return this; + } + + /** + * Writes an attribute with a URI value to the output: <code><xa>ns:name</xa>=<xs>'uri-value'</xs></code> + * + * @param ns The namespace. Can be <jk>null</jk>. + * @param name The attribute name. + * @param value The attribute value, convertable to a URI via <code>toString()</code> + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter attrUri(String ns, String name, Object value) throws IOException { + oAttr(ns, name).q().appendUri(value).q(); + return this; + } + + /** + * Serializes and encodes the specified object as valid XML text. + * + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter encodeText(Object o) throws IOException { + XmlUtils.encodeText(this, o); + return this; + } + + /** + * Serializes and encodes the specified object as valid XML text. + * <p> + * Does NOT encode XML characters (<js>'<'</js>, <js>'>'</js>, and <js>'&'</js>). + * <p> + * Use on XML text that you just want to replace invalid XML characters with <js>"_x####_"</js> sequences. + * + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter encodeTextInvalidChars(Object o) throws IOException { + XmlUtils.encodeTextInvalidChars(this, o); + return this; + } + + /** + * Serializes and encodes the specified object as valid XML text. + * <p> + * Only encodes XML characters (<js>'<'</js>, <js>'>'</js>, and <js>'&'</js>). + * <p> + * Use on XML text where the invalid characters have already been replaced. + * + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter encodeTextXmlChars(Object o) throws IOException { + XmlUtils.encodeTextXmlChars(this, o); + return this; + } + + /** + * Serializes and encodes the specified object as valid XML attribute name. + * + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter encodeAttr(Object o) throws IOException { + XmlUtils.encodeAttr(out, o); + return this; + } + + /** + * Serializes and encodes the specified object as valid XML element name. + * + * @param o The object being serialized. + * @return This object (for method chaining). + * @throws IOException If a problem occurred. + */ + public XmlWriter encodeElement(Object o) throws IOException { + XmlUtils.encodeElementName(out, o); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter cr(int depth) throws IOException { + super.cr(depth); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter appendln(int indent, String text) throws IOException { + super.appendln(indent, text); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter appendln(String text) throws IOException { + super.appendln(text); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter append(int indent, String text) throws IOException { + super.append(indent, text); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter append(int indent, char c) throws IOException { + super.append(indent, c); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter s() throws IOException { + super.s(); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter q() throws IOException { + super.q(); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter i(int indent) throws IOException { + super.i(indent); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter nl() throws IOException { + super.nl(); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter append(Object text) throws IOException { + super.append(text); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter append(String text) throws IOException { + super.append(text); + return this; + } + + @Override /* SerializerWriter */ + public XmlWriter append(char c) throws IOException { + out.write(c); + return this; + } + + @Override /* Object */ + public String toString() { + return out.toString(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java new file mode 100644 index 0000000..25cfc74 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/Xml.java @@ -0,0 +1,201 @@ +/*************************************************************************************************************************** + * 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.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +import org.apache.juneau.xml.*; + +/** + * Annotation for specifying various XML options for the XML and RDF/XML serializers. + * <p> + * Can be applied to Java packages, types, fields, and methods. + * <p> + * Can be used for the following: + * <ul class='spaced-list'> + * <li>Override the element name on the XML representation of a bean or object. + * <li>Override the child element name on the XML representation of collection or array properties. + * <li>Specify the XML namespace on a package, class, or method. + * <li>Override the XML format on a POJO. + * </ul> + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target({TYPE,FIELD,METHOD}) +@Retention(RUNTIME) +@Inherited +public @interface Xml { + + /** + * Sets the name of the XML element in cases where the XML element has no name (e.g. the root element). + * <p> + * Applies only to {@link ElementType#TYPE}. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <ja>@Xml</ja>(name=<js>"MyBean"</js>) + * <jk>public class</jk> MyBean { + * <jk>public int</jk> f1 = 123; + * } + * </p> + * <p> + * Without the <ja>@Xml</ja> annotations, serializing this bean as XML would have produced the following... + * </p> + * <p class='bcode'> + * <xt><object></xt> + * <xt><f1></xt>123<xt></f1></xt> + * <xt></object></xt> + * </p> + * <p> + * With the annotations, serializing this bean as XML produces the following... + * </p> + * <p class='bcode'> + * <xt><MyBean></xt> + * <xt><f1></xt>123<xt></f1></xt> + * <xt></MyBean></xt> + * </p> + * </dd> + * </dl> + */ + String name() default ""; + + /** + * Sets the name of the XML child elements for bean properties of type collection and array. + * <p> + * Applies only to collection and array bean properties. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jk>public class</jk> MyBean { + * <ja>@Xml</ja>(childName=<js>"child"</js>} + * <jk>public</jk> String[] <jf>children</jf> = {<js>"foo"</js>,<js>"bar"</js>}; + * } + * </p> + * <p> + * Without the <ja>@Xml</ja> annotation, serializing this bean as XML would have produced the following... + * </p> + * <p class='bcode'> + * <xt><object></xt> + * <xt><children></xt> + * <xt><string></xt>foo<xt></string></xt> + * <xt><string></xt>bar<xt></string></xt> + * <xt></children></xt> + * <xt></object></xt> + * </p> + * <p> + * With the annotations, serializing this bean as XML produces the following... + * </p> + * <p class='bcode'> + * <xt><object></xt> + * <xt><children></xt> + * <xt><child></xt>foo<xt></child></xt> + * <xt><child></xt>bar<xt></child></xt> + * <xt></children></xt> + * <xt></object></xt> + * </p> + * </dd> + * </dl> + */ + String childName() default ""; + + /** + * Sets the XML prefix of this property or class. + * <ul class='spaced-list'> + * <li>When applied to a {@link ElementType#TYPE}, namespace is applied to all properties in the class, and all + * subclasses of the class. + * <li>When applied to bean properties on {@link ElementType#METHOD} and {@link ElementType#FIELD}, applies + * to the bean property. + * </ul> + * <p> + * Must either be matched to a {@link #namespace()} annotation on the same object, parent object, or a {@link XmlNs} with the same name + * through the {@link XmlSchema#xmlNs()} annotation on the package. + * </p> + */ + String prefix() default ""; + + /** + * Sets the namespace URI of this property or class. + * <p> + * Must be matched with a {@link #prefix()} annotation on this object, a parent object, or a {@link XmlNs} with the same name + * through the {@link XmlSchema#xmlNs()} annotation on the package. + */ + String namespace() default ""; + + /** + * The {@link XmlFormat} to use for serializing this object type. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jk>public class</jk> MyBean { + * + * <jc>// Normally, bean properties would be rendered as child elements of the bean element.</jc> + * <jc>// Override so that it's rendered as a "f1='123'" attribute on the bean element instead.</jc> + * <ja>@Xml</ja>(format=XmlFormat.<jsf>ATTR</jsf>} + * <jk>public int</jk> f1 = 123; + * + * <jc>// Normally, bean URL properties would be rendered as XML attributes on the bean element.</jc> + * <jc>// Override so that it's rendered as an <href>http://foo</href> child element instead.</jc> + * <ja>@BeanProperty</ja>(uri=<jk>true</jk>) + * <ja>@Xml</ja>(format=XmlFormat.<jsf>ELEMENT</jsf>} + * <jk>public</jk> URL <jf>href</jf> = <jk>new</jk> URL(<js>"http://foo"</js>); + * + * <jc>// Normally, collection properties would be grouped under a single <children> child element on the bean element.</jc> + * <jc>// Override so that entries are directly children of the bean element with each entry having an element name of <child>.</jc> + * <ja>@Xml</ja>(format=XmlFormat.<jsf>COLLAPSED</jsf>, childName=<js>"child"</js>} + * <jk>public</jk> String[] <jf>children</jf> = <js>"foo"</js>,<js>"bar"</js>}; + * } + * </p> + * <p> + * Without the <ja>@Xml</ja> annotations, serializing this bean as XML would have produced the following... + * </p> + * <p class='bcode'> + * <xt><object</xt> <xa>href</xa>=<js>'http://foo'</js><xt>></xt> + * <xt><f1></xt>123<xt></f1></xt> + * <xt><children></xt> + * <xt><string></xt>foo<xt></string></xt> + * <xt><string></xt>bar<xt></string></xt> + * <xt></children></xt> + * <xt></object></xt> + * </p> + * <p> + * With the annotations, serializing this bean as XML produces the following... + * </p> + * <p class='bcode'> + * <xt><object</xt> <xa>f1</xa>=<js>'123'</js><xt>></xt> + * <xt><href></xt>http://foo<xt></href></xt> + * <xt><child></xt>foo<xt></child></xt> + * <xt><child></xt>bar<xt></child></xt> + * <xt></object></xt> + * </p> + * </dd> + * </dl> + */ + XmlFormat format() default XmlFormat.NORMAL; + + /** + * Associates a content handler with a bean class or bean property. + * <p> + * Refer to {@link XmlContentHandler} for more information. + */ + Class<? extends XmlContentHandler<?>> contentHandler() default XmlContentHandler.NULL.class; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java new file mode 100644 index 0000000..24d4d73 --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlFormat.java @@ -0,0 +1,62 @@ +/*************************************************************************************************************************** + * 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.annotation; + +/** + * XML format to use when serializing a POJO. + * + * @author James Bognar ([email protected]) + */ +public enum XmlFormat { + + /** + * Normal formatting (default) + */ + NORMAL, + + /** + * Render property as an attribute instead of an element. + * <p> + * Can only be applied to properties (methods/fields) of simple types (e.g. <code>String</code>, <code>Number</code>). + */ + ATTR, + + /** + * Render property as an element instead of an attribute. + * <p> + * Can be applied to URL and ID bean properties that would normally be rendered as attributes. + */ + ELEMENT, + + /** + * Prevents collections and arrays from being enclosed in <xt><array></xt> elements. + * <p> + * Can only be applied to properties (methods/fields) of type collection or array, or collection classes. + */ + COLLAPSED, + + /** + * Render property value directly as content of element. + * <p> + * By default, content is converted to plain text. + * <p> + * Can be used in combination with {@link Xml#contentHandler()} to produce something other + * than plain text, such as embedded XML. + */ + CONTENT, + + /** + * Render a collection/array as mixed child content of the element. + */ + MIXED +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlNs.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlNs.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlNs.java new file mode 100644 index 0000000..d41d64d --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlNs.java @@ -0,0 +1,41 @@ +/*************************************************************************************************************************** + * 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.annotation; + +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +/** + * Namespace name/URL mapping pair. + * <p> + * Used to identify a namespace/URI pair on a {@link XmlSchema#xmlNs()} annotation. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target({}) +@Retention(RUNTIME) +@Inherited +public @interface XmlNs { + + /** + * XML namespace prefix. + */ + String prefix(); + + /** + * XML namespace URL. + */ + String namespaceURI(); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlSchema.java ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlSchema.java b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlSchema.java new file mode 100644 index 0000000..b701bfe --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/XmlSchema.java @@ -0,0 +1,98 @@ +/*************************************************************************************************************************** + * 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.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +/** + * Identifies the default XML namespaces at the package level. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target(PACKAGE) +@Retention(RUNTIME) +@Inherited +public @interface XmlSchema { + + /** + * Sets the default XML prefix for all classes in this and child packages. + * <p> + * Must either be matched with a {@link #namespace()} annotation, or an {@link #xmlNs()} mapping with the + * same {@link XmlNs#prefix} value. + * </p> + */ + public String prefix() default ""; + + /** + * Sets the default XML namespace URL for all classes in this and child packages. + * <p> + * Must either be matched with a {@link #prefix()} annotation, or an {@link #xmlNs()} mapping with the + * same {@link XmlNs#namespaceURI} value. + * </p> + */ + public String namespace() default ""; + + /** + * Lists all namespace mappings to be used on all classes within this package. + * <p> + * The purpose of this annotation is to allow namespace mappings to be defined in a single location + * and referred to by name through just the {@link Xml#prefix()} annotation. + * <p> + * Inherited by child packages. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p> + * Contents of <code>package-info.java</code>... + * </p> + * <p class='bcode'> + * <jc>// XML namespaces used within this package.</jc> + * <ja>@XmlSchema</ja>(prefix=<js>"ab"</js>, + * namespaces={ + * <ja>@XmlNs</ja>(prefix=<js>"ab"</js>, namespaceURI=<js>"http://www.ibm.com/addressBook/"</js>), + * <ja>@XmlNs</ja>(prefix=<js>"per"</js>, namespaceURI=<js>"http://www.ibm.com/person/"</js>), + * <ja>@XmlNs</ja>(prefix=<js>"addr"</js>, namespaceURI=<js>"http://www.ibm.com/address/"</js>), + * <ja>@XmlNs</ja>(prefix=<js>"mail"</js>, namespaceURI="<js>http://www.ibm.com/mail/"</js>) + * } + * ) + * <jk>package</jk> com.ibm.sample.addressbook; + * <jk>import</jk> org.apache.juneau.xml.annotation.*; + * </p> + * <p> + * Class in package using defined namespaces... + * </p> + * <p class='bcode'> + * <jk>package</jk> com.ibm.sample.addressbook; + * + * <jc>// Bean class, override "ab" namespace on package.</jc> + * <ja>@Xml</ja>(prefix=<js>"addr"</js>) + * <jk>public class</jk> Address { + * + * <jc>// Bean property, use "addr" namespace on class.</jc> + * <jk>public int</jk> <jf>id</jf>; + * + * <jc>// Bean property, override with "mail" namespace.</jc> + * <ja>@Xml</ja>(prefix=<js>"mail"</js>) + * <jk>public</jk> String <jf>street</jf>, <jf>city</jf>, <jf>state</jf>; + * } + * </p> + * </dd> + * </dl> + */ + public XmlNs[] xmlNs() default {}; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/annotation/package.html ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/annotation/package.html b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/package.html new file mode 100644 index 0000000..662281a --- /dev/null +++ b/juneau-core/src/main/java/org/apache/juneau/xml/annotation/package.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<!-- +/*************************************************************************************************************************** + * 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. + * + ***************************************************************************************************************************/ + --> +<html> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <style type="text/css"> + /* For viewing in Page Designer */ + @IMPORT url("../../../../../../../javadoc.css"); + + /* For viewing in REST interface */ + @IMPORT url("../htdocs/javadoc.css"); + body { + margin: 20px; + } + </style> + <script> + /* Replace all @code and @link tags. */ + window.onload = function() { + document.body.innerHTML = document.body.innerHTML.replace(/\{\@code ([^\}]+)\}/g, '<code>$1</code>'); + document.body.innerHTML = document.body.innerHTML.replace(/\{\@link (([^\}]+)\.)?([^\.\}]+)\}/g, '<code>$3</code>'); + } + </script> +</head> +<body> +<p>XML annotations</p> +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_HTML.png ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_HTML.png b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_HTML.png new file mode 100644 index 0000000..b4a3576 Binary files /dev/null and b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_HTML.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XML.png ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XML.png b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XML.png new file mode 100644 index 0000000..bfced84 Binary files /dev/null and b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XML.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSchema.png ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSchema.png b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSchema.png new file mode 100644 index 0000000..8a64e89 Binary files /dev/null and b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSchema.png differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/e6bf97a8/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSimple.png ---------------------------------------------------------------------- diff --git a/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSimple.png b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSimple.png new file mode 100644 index 0000000..7acba29 Binary files /dev/null and b/juneau-core/src/main/java/org/apache/juneau/xml/doc-files/Example_XMLSimple.png differ
