You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2016/08/09 19:53:44 UTC
[11/51] [partial] incubator-juneau git commit: Rename project
directories.
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 (james.bognar@salesforce.com)
+ */
+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 (james.bognar@salesforce.com)
+ */
+@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 (james.bognar@salesforce.com)
+ */
+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 (james.bognar@salesforce.com)
+ */
+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 (james.bognar@salesforce.com)
+ */
+@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 (james.bognar@salesforce.com)
+ */
+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 (james.bognar@salesforce.com)
+ */
+@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 (james.bognar@salesforce.com)
+ */
+@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