http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeOutputStream.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeOutputStream.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeOutputStream.java new file mode 100755 index 0000000..70807a8 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeOutputStream.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.utils; + +import java.io.*; +import java.util.*; + +/** + * Output stream that can send output to multiple output streams. + * + * @author James Bognar ([email protected]) + */ +public class TeeOutputStream extends OutputStream { + private OutputStream[] outputStreams = new OutputStream[0]; + private Map<String,OutputStream> outputStreamMap; + + /** + * Constructor. + * + * @param outputStreams The list of output streams. + */ + public TeeOutputStream(OutputStream...outputStreams) { + this.outputStreams = outputStreams; + } + + /** + * Constructor. + * + * @param outputStreams The list of output streams. + */ + public TeeOutputStream(Collection<OutputStream> outputStreams) { + this.outputStreams = outputStreams.toArray(new OutputStream[outputStreams.size()]); + } + + /** + * Adds an output stream to this tee output stream. + * + * @param os The output stream to add to this tee output stream. + * @param close If <jk>false</jk>, then calling {@link #close()} on this stream + * will not filter to the specified output stream. + * @return This object (for method chaining). + */ + public TeeOutputStream add(OutputStream os, boolean close) { + if (os == null) + return this; + if (! close) + os = new NoCloseOutputStream(os); + if (os == this) + throw new RuntimeException("Cannot add this output stream to itself."); + for (OutputStream os2 : outputStreams) + if (os2 == os) + throw new RuntimeException("Cannot add this output stream again."); + if (os instanceof TeeOutputStream) { + for (OutputStream os2 : ((TeeOutputStream)os).outputStreams) + add(os2, true); + } else { + outputStreams = ArrayUtils.append(outputStreams, os); + } + return this; + } + + /** + * Returns the output stream identified through the <code>id</code> parameter + * passed in through the {@link #add(String, OutputStream, boolean)} method. + * + * @param id The ID associated with the output stream. + * @return The output stream, or <jk>null</jk> if no identifier was specified when the output stream was added. + */ + public OutputStream getOutputStream(String id) { + if (outputStreamMap != null) + return outputStreamMap.get(id); + return null; + } + + /** + * Same as {@link #add(OutputStream, boolean)} but associates the stream with an identifier + * so the stream can be retrieved through {@link #getOutputStream(String)}. + * + * @param id The ID to associate the output stream with. + * @param os The output stream to add. + * @param close Close the specified stream afterwards. + * @return This object (for method chaining). + */ + public TeeOutputStream add(String id, OutputStream os, boolean close) { + if (id != null) { + if (outputStreamMap == null) + outputStreamMap = new TreeMap<String,OutputStream>(); + outputStreamMap.put(id, os); + } + return add(os, close); + } + + /** + * Returns the number of inner streams in this tee stream. + * + * @return The number of streams in this tee stream. + */ + public int size() { + return outputStreams.length; + } + + @Override /* OutputStream */ + public void write(int b) throws IOException { + for (OutputStream os : outputStreams) + os.write(b); + } + + @Override /* OutputStream */ + public void write(byte b[], int off, int len) throws IOException { + for (OutputStream os : outputStreams) + os.write(b, off, len); + } + + @Override /* OutputStream */ + public void flush() throws IOException { + for (OutputStream os : outputStreams) + os.flush(); + } + + @Override /* OutputStream */ + public void close() throws IOException { + for (OutputStream os : outputStreams) + os.close(); + } + + private static class NoCloseOutputStream extends OutputStream { + private OutputStream os; + + private NoCloseOutputStream(OutputStream os) { + this.os = os; + } + + @Override /* OutputStream */ + public void write(int b) throws IOException { + os.write(b); + } + + @Override /* OutputStream */ + public void write(byte b[], int off, int len) throws IOException { + os.write(b, off, len); + } + + @Override /* OutputStream */ + public void flush() throws IOException { + os.flush(); + } + + @Override /* OutputStream */ + public void close() throws IOException { + // Do nothing. + } + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$1.class new file mode 100755 index 0000000..caa1961 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$1.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$NoCloseWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$NoCloseWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$NoCloseWriter.class new file mode 100755 index 0000000..fc76e14 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter$NoCloseWriter.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.class new file mode 100755 index 0000000..b5702ac Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.java new file mode 100755 index 0000000..f01c3cd --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/TeeWriter.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.utils; + +import java.io.*; +import java.util.*; + + +/** + * Writer that can send output to multiple writers. + * + * @author James Bognar ([email protected]) + */ +public class TeeWriter extends Writer { + private Writer[] writers = new Writer[0]; + private Map<String,Writer> writerMap; + + /** + * Constructor. + * + * @param writers The list of writers. + */ + public TeeWriter(Writer...writers) { + this.writers = writers; + } + + /** + * Constructor. + * + * @param writers The list of writers. + */ + public TeeWriter(Collection<Writer> writers) { + this.writers = writers.toArray(new Writer[writers.size()]); + } + + /** + * Adds a writer to this tee writer. + * + * @param w The writer to add to this tee writer. + * @param close If <jk>false</jk>, then calling {@link #close()} on this tee writer + * will not filter to the specified writer. + * @return This object (for method chaining). + */ + public TeeWriter add(Writer w, boolean close) { + if (w == null) + return this; + if (! close) + w = new NoCloseWriter(w); + if (w == this) + throw new RuntimeException("Cannot add this writer to itself."); + for (Writer w2 : writers) + if (w2 == w) + throw new RuntimeException("Cannot add this writer again."); + if (w instanceof TeeWriter) { + for (Writer w2 : ((TeeWriter)w).writers) + add(w2, true); + } else { + writers = ArrayUtils.append(writers, w); + } + return this; + } + + /** + * Same as {@link #add(Writer, boolean)} but associates the writer with an identifier + * so the writer can be retrieved through {@link #getWriter(String)}. + * + * @param id The ID to associate the writer with. + * @param w The writer to add. + * @param close Close the specified writer afterwards. + * @return This object (for method chaining). + */ + public TeeWriter add(String id, Writer w, boolean close) { + if (id != null) { + if (writerMap == null) + writerMap = new TreeMap<String,Writer>(); + writerMap.put(id, w); + } + return add(w, close); + } + + /** + * Returns the number of inner writers in this tee writer. + * + * @return The number of writers. + */ + public int size() { + return writers.length; + } + + /** + * Returns the writer identified through the <code>id</code> parameter + * passed in through the {@link #add(String, Writer, boolean)} method. + * + * @param id The ID associated with the writer. + * @return The writer, or <jk>null</jk> if no identifier was specified when the writer was added. + */ + public Writer getWriter(String id) { + if (writerMap != null) + return writerMap.get(id); + return null; + } + + @Override /* Writer */ + public void write(char[] cbuf, int off, int len) throws IOException { + for (Writer w : writers) + if (w != null) + w.write(cbuf, off, len); + } + + @Override /* Writer */ + public void flush() throws IOException { + for (Writer w : writers) + if (w != null) + w.flush(); + } + + @Override /* Writer */ + public void close() throws IOException { + IOException e = null; + for (Writer w : writers) { + if (w != null) { + try { + w.close(); + } catch (IOException e2) { + e = e2; + } + } + } + if (e != null) + throw e; + } + + private static class NoCloseWriter extends Writer { + private Writer writer; + + private NoCloseWriter(Writer writer) { + this.writer = writer; + } + + @Override /* Writer */ + public void write(char[] cbuf, int off, int len) throws IOException { + writer.write(cbuf, off, len); + } + + @Override /* Writer */ + public void flush() throws IOException { + writer.flush(); + } + + @Override /* Writer */ + public void close() throws IOException { + // Do nothing. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.class new file mode 100755 index 0000000..55805dd Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.java new file mode 100755 index 0000000..d2b25ae --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ThrowableUtils.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.utils; + +import java.text.*; + +/** + * Various utility methods for creating and working with throwables. + * + * @author James Bognar ([email protected]) + */ +public class ThrowableUtils { + + /** + * Throws an {@link IllegalArgumentException} if the specified object is <jk>null</jk>. + * + * @param o The object to check. + * @param msg The message of the IllegalArgumentException. + * @param args {@link MessageFormat}-style arguments in the message. + * @throws IllegalArgumentException + */ + public static void assertNotNull(Object o, String msg, Object...args) throws IllegalArgumentException { + if (o == null) + throw new IllegalArgumentException(MessageFormat.format(msg, args)); + } + + /** + * Throws an {@link IllegalArgumentException} if the specified field is <jk>null</jk>. + * + * @param fieldValue The object to check. + * @param fieldName The name of the field. + * @throws IllegalArgumentException + */ + public static void assertFieldNotNull(Object fieldValue, String fieldName) throws IllegalArgumentException { + if (fieldValue == null) + throw new IllegalArgumentException("Field '" + fieldName + "' cannot be null."); + } + + /** + * Throws an {@link IllegalArgumentException} if the specified field is <code><=0</code>. + * + * @param fieldValue The object to check. + * @param fieldName The name of the field. + * @throws IllegalArgumentException + */ + public static void assertFieldPositive(int fieldValue, String fieldName) throws IllegalArgumentException { + if (fieldValue <= 0) + throw new IllegalArgumentException("Field '" + fieldName + "' must be a positive integer."); + } + + /** + * Shortcut for calling <code><jk>new</jk> IllegalArgumentException(MessageFormat.<jsm>format</jsm>(msg, args));</code> + * + * @param msg The message of the IllegalArgumentException. + * @param args {@link MessageFormat}-style arguments in the message. + * @throws IllegalArgumentException + */ + public static void illegalArg(String msg, Object...args) throws IllegalArgumentException { + throw new IllegalArgumentException(MessageFormat.format(msg, args)); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$FileEntry.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$FileEntry.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$FileEntry.class new file mode 100755 index 0000000..4b11004 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$FileEntry.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$ZipFileEntry.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$ZipFileEntry.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$ZipFileEntry.class new file mode 100755 index 0000000..bad51b4 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList$ZipFileEntry.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.class new file mode 100755 index 0000000..77ab270 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.java new file mode 100755 index 0000000..1aebf3c --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/ZipFileList.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: + * Use, duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core.utils; + +import java.io.*; +import java.util.*; +import java.util.zip.*; + +/** + * Utility class for representing the contents of a zip file as a list of entries + * whose contents don't resolve until serialize time. + * <p> + * Generally associated with <code>RestServlets</code> using the <code>responseHandlers</code> + * annotation so that REST methods can easily create ZIP file responses by simply returning instances + * of this class. + */ +@SuppressWarnings("serial") +public class ZipFileList extends LinkedList<ZipFileList.ZipFileEntry> { + + /** + * The name of the zip file. + */ + public final String fileName; + + /** + * Constructor. + * + * @param fileName The file name of the zip file to create. + */ + public ZipFileList(String fileName) { + this.fileName = fileName; + } + + /** + * Add an entry to this list. + * + * @param e The zip file entry. + * @return This object (for method chaining). + */ + public ZipFileList append(ZipFileEntry e) { + add(e); + return this; + } + + /** + * Interface for ZipFileList entries. + */ + public static interface ZipFileEntry { + /** + * Write this entry to the specified output stream. + * + * @param zos The output stream to write to. + * @throws IOException + */ + void write(ZipOutputStream zos) throws IOException; + } + + /** + * ZipFileList entry for File entry types. + */ + public static class FileEntry implements ZipFileEntry { + + /** The root file to base the entry paths on. */ + protected File root; + + /** The file being zipped. */ + protected File file; + + /** + * Constructor. + * + * @param root The root file that represents the base path. + * @param file The file to add to the zip file. + */ + public FileEntry(File root, File file) { + this.root = root; + this.file = file; + } + + /** + * Constructor. + * + * @param file The file to add to the zip file. + */ + public FileEntry(File file) { + this.file = file; + this.root = (file.isDirectory() ? file : file.getParentFile()); + } + + @Override /* ZipFileEntry */ + public void write(ZipOutputStream zos) throws IOException { + addFile(zos, file); + } + + /** + * Subclasses can override this method to customize which files get added to a zip file. + * + * @param f The file being added to the zip file. + * @return Always returns <jk>true</jk>. + */ + public boolean doAdd(File f) { + return true; + } + + /** + * Adds the specified file to the specified output stream. + * + * @param zos The output stream. + * @param f The file to add. + * @throws IOException + */ + protected void addFile(ZipOutputStream zos, File f) throws IOException { + if (doAdd(f)) { + if (f.isDirectory()) { + File[] fileList = f.listFiles(); + if (fileList == null) + throw new IOException(f.toString()); + for (File fc : fileList) + addFile(zos, fc); + } else if (f.canRead()) { + String path = f.getAbsolutePath().substring(root.getAbsolutePath().length() + 1).replace('\\', '/'); + ZipEntry e = new ZipEntry(path); + e.setSize(f.length()); + zos.putNextEntry(e); + IOPipe.create(new FileInputStream(f), zos).run(); + } + } + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/package.html new file mode 100755 index 0000000..97bdc68 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/utils/package.html @@ -0,0 +1,53 @@ +<!DOCTYPE HTML> +<!-- + Licensed Materials - Property of IBM + (c) Copyright IBM Corporation 2014. All Rights Reserved. + + Note to U.S. Government Users Restricted Rights: + Use, duplication or disclosure restricted by GSA ADP Schedule + Contract with IBM Corp. + --> +<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>Utility classes</p> + +<script> + function toggle(x) { + var div = x.nextSibling; + while (div != null && div.nodeType != 1) + div = div.nextSibling; + if (div != null) { + var d = div.style.display; + if (d == 'block' || d == '') { + div.style.display = 'none'; + x.className += " closed"; + } else { + div.style.display = 'block'; + x.className = x.className.replace(/(?:^|\s)closed(?!\S)/g , '' ); + } + } + } +</script> + +</body> +</html> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.class new file mode 100755 index 0000000..281438e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.java new file mode 100755 index 0000000..7cb3b54 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/Namespace.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +/** + * Represents a simple namespace mapping between a simple name and URI. + * <p> + * In general, the simple name will be used as the XML prefix mapping unless + * there are conflicts or prefix remappings in the serializer. + * + * @author James Bognar ([email protected]) + */ +public final class Namespace implements Comparable<Namespace> { + final String name, uri; + private final int hashCode; + + /** + * Constructor. + * <p> + * Use this constructor when the long name and short name are the same value. + * + * @param name The long and short name of this schema. + * @param uri The URI of this schema. + */ + protected Namespace(String name, String uri) { + this.name = name; + this.uri = uri; + this.hashCode = (name == null ? 0 : name.hashCode()) + uri.hashCode(); + } + + /** + * Returns the namespace name. + * + * @return The namespace name. + */ + public String getName() { + return name; + } + + /** + * Returns the namespace URI. + * + * @return The namespace URI. + */ + public String getUri() { + return uri; + } + + @Override /* Comparable */ + public int compareTo(Namespace o) { + int i = name.compareTo(o.name); + if (i == 0) + i = uri.compareTo(o.uri); + return i; + } + + /** + * For performance reasons, equality is always based on identity, since + * the {@link NamespaceFactory} class ensures no duplicate name+uri pairs. + */ + @Override /* Object */ + public boolean equals(Object o) { + return this == o; + } + + @Override /* Object */ + public int hashCode() { + return hashCode; + } + + @Override /* Object */ + public String toString() { + return "{name='"+name+"',uri:'"+uri+"'}"; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.class new file mode 100755 index 0000000..27b3171 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.java new file mode 100755 index 0000000..20faa03 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/NamespaceFactory.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import java.util.*; +import java.util.concurrent.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.utils.*; + +/** + * Factory class for getting unique instances of {@link Namespace} objects. + * <p> + * For performance reasons, {@link Namespace} objects are stored in {@link IdentityList IdentityLists}. + * For this to work property, namespaces with the same name and URI must only be represented by a single + * {@link Namespace} instance. + * This factory class ensures this identity uniqueness. + * + * @author James Bognar ([email protected]) + */ +public final class NamespaceFactory { + + private static ConcurrentHashMap<String,Namespace> cache = new ConcurrentHashMap<String,Namespace>(); + + /** + * Get the {@link Namespace} with the specified name and URI, and create a new one + * if this is the first time it's been encountered. + * + * @param name The namespace name. See {@link Namespace#getName()}. + * @param uri The namespace URI. See {@link Namespace#getUri()}. + * @return The namespace object. + */ + public static Namespace get(String name, String uri) { + String key = name + "+" + uri; + Namespace n = cache.get(key); + if (n == null) { + n = new Namespace(name, uri); + Namespace n2 = cache.putIfAbsent(key, n); + return (n2 == null ? n : n2); + } + return n; + } + + /** + * Converts the specified object into a {@link Namespace} object. + * <p> + * Can be any of following types: + * <ul> + * <li>A {@link Namespace} object + * <li>A JSON string containing a single key/value pair indicating the name/URI mapping. + * <li>A <code>Map</code> containing a single key/value pair indicating the name/URI mapping. + * </ul> + * + * @param o The input. + * @return The namespace object, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object. + */ + @SuppressWarnings("rawtypes") + public static Namespace parseNamespace(Object o) { + if (o == null) + return null; + if (o instanceof Namespace) + return (Namespace)o; + try { + Map<?,?> m = (o instanceof Map ? (Map)o : new ObjectMap(o.toString())); + if (m.size() == 0) + return null; + if (m.size() > 1) + throw new RuntimeException("Too many namespaces specified. Only one allowed. '"+o+"'"); + Map.Entry<?,?> e = m.entrySet().iterator().next(); + return get(e.getKey().toString(), e.getValue().toString()); + } catch (ParseException e) { + throw new RuntimeException(e); + } + } + + /** + * Converts the specified object into an array of {@link Namespace} object. + * <p> + * Can be any of following types: + * <ul> + * <li>A {@link Namespace} array + * <li>A JSON string with key/value pairs indicating name/URI pairs. + * <li>A <code>Map</code> with key/value pairs indicating name/URI pairs. + * <li>A <code>Collection</code> containing any of object that can be passed to {@link #parseNamespace(Object)}. + * </ul> + * + * @param o The input. + * @return The namespace objects, or <jk>null</jk> if the input was <jk>null</jk> or an empty JSON object. + */ + @SuppressWarnings("rawtypes") + public static Namespace[] parseNamespaces(Object o) { + try { + if (o instanceof Namespace[]) + return (Namespace[])o; + + if (o instanceof CharSequence) + o = new ObjectMap(o.toString()); + + Namespace[] n; + int i = 0; + if (o instanceof Collection) { + Collection c = (Collection)o; + n = new Namespace[c.size()]; + for (Object o2 : c) + n[i++] = parseNamespace(o2); + } else if (o instanceof Map) { + Map<?,?> m = (Map<?,?>)o; + n = new Namespace[m.size()]; + for (Map.Entry e : m.entrySet()) + n[i++] = get(e.getKey().toString(), e.getValue().toString()); + } else { + throw new RuntimeException("Invalid type passed to NamespaceFactory.listFromObject: '"+o+"'"); + } + return n; + } catch (ParseException e) { + throw new RuntimeException(e); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.class new file mode 100755 index 0000000..507530b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.java new file mode 100755 index 0000000..3ffbe5c --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanMeta.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.xml.annotation.*; + +/** + * Metadata on beans specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the class. + * + * @author James Bognar ([email protected]) + * @param <T> The bean class type. + */ +public class XmlBeanMeta<T> { + + // XML related fields + private final Map<String,BeanPropertyMeta<T>> xmlAttrs; // Map of bean properties that are represented as XML attributes. + private final BeanPropertyMeta<T> xmlContent; // Bean property that is represented as XML content within the bean element. + private final XmlContentHandler<T> xmlContentHandler; // Class used to convert bean to XML content. + private final Map<String,BeanPropertyMeta<T>> childElementProperties; // Properties defined with @Xml.childName annotation. + private final BeanMeta<T> beanMeta; + + /** + * Constructor. + * + * @param beanMeta The metadata on the bean that this metadata applies to. + * @param pNames Only look at these property names. If <jk>null</jk>, apply to all bean properties. + */ + public XmlBeanMeta(BeanMeta<T> beanMeta, String[] pNames) { + this.beanMeta = beanMeta; + Class<T> c = beanMeta.getClassMeta().getInnerClass(); + + Map<String,BeanPropertyMeta<T>> tXmlAttrs = new LinkedHashMap<String,BeanPropertyMeta<T>>(); + BeanPropertyMeta<T> tXmlContent = null; + XmlContentHandler<T> tXmlContentHandler = null; + Map<String,BeanPropertyMeta<T>> tChildElementProperties = new LinkedHashMap<String,BeanPropertyMeta<T>>(); + + for (BeanPropertyMeta<T> p : beanMeta.getPropertyMetas(pNames)) { + XmlFormat xf = p.getXmlMeta().getXmlFormat(); + if (xf == XmlFormat.ATTR) + tXmlAttrs.put(p.getName(), p); + else if (xf == XmlFormat.CONTENT) { + if (tXmlContent != null) + throw new BeanRuntimeException(c, "Multiple instances of CONTENT properties defined on class. Only one property can be designated as such."); + tXmlContent = p; + tXmlContentHandler = p.getXmlMeta().getXmlContentHandler(); + } + // Look for any properties that are collections with @Xml.childName specified. + String n = p.getXmlMeta().getChildName(); + if (n != null) { + if (tChildElementProperties.containsKey(n)) + throw new BeanRuntimeException(c, "Multiple properties found with the name ''{0}''.", n); + tChildElementProperties.put(n, p); + } + } + + xmlAttrs = Collections.unmodifiableMap(tXmlAttrs); + xmlContent = tXmlContent; + xmlContentHandler = tXmlContentHandler; + childElementProperties = (tChildElementProperties.isEmpty() ? null : Collections.unmodifiableMap(tChildElementProperties)); + } + + /** + * Returns the list of properties annotated with an {@link Xml#format()} of {@link XmlFormat#ATTR}. + * In other words, the list of properties that should be rendered as XML attributes instead of child elements. + * + * @return Metadata on the XML attribute properties of the bean. + */ + protected Map<String,BeanPropertyMeta<T>> getXmlAttrProperties() { + return xmlAttrs; + } + + /** + * Returns the bean property annotated with an {@link Xml#format()} value of {@link XmlFormat#CONTENT} + * + * @return The bean property, or <jk>null</jk> if annotation is not specified. + */ + protected BeanPropertyMeta<T> getXmlContentProperty() { + return xmlContent; + } + + /** + * Return the XML content handler for this bean. + * + * @return The XML content handler for this bean, or <jk>null</jk> if no content handler is defined. + */ + protected XmlContentHandler<T> getXmlContentHandler() { + return xmlContentHandler; + } + + /** + * Returns the child element properties for this bean. + * See {@link Xml#childName()} + * + * @return The child element properties for this bean, or <jk>null</jk> if no child element properties are defined. + */ + protected Map<String,BeanPropertyMeta<T>> getChildElementProperties() { + return childElementProperties; + } + + /** + * Returns bean property meta with the specified name. + * This is identical to calling {@link BeanMeta#getPropertyMeta(String)} except it first retrieves + * the bean property meta based on the child name (e.g. a property whose name is "people", but whose child name is "person"). + * + * @param fieldName The bean property name. + * @return The property metadata. + */ + protected BeanPropertyMeta<T> getPropertyMeta(String fieldName) { + if (childElementProperties != null) { + BeanPropertyMeta<T> bpm = childElementProperties.get(fieldName); + if (bpm != null) + return bpm; + } + return beanMeta.getPropertyMeta(fieldName); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.class new file mode 100755 index 0000000..f3e5a97 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.java new file mode 100755 index 0000000..210ae0b --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlBeanPropertyMeta.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import java.util.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.xml.annotation.*; + +/** + * Metadata on bean properties specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the bean property. + * + * @author James Bognar ([email protected]) + * @param <T> The bean class. + */ +public class XmlBeanPropertyMeta<T> { + + private Namespace namespace = null; + private XmlFormat xmlFormat = XmlFormat.NORMAL; + private XmlContentHandler<T> xmlContentHandler = null; + private String childName; + private final BeanPropertyMeta<T> beanPropertyMeta; + + /** + * Constructor. + * + * @param beanPropertyMeta The metadata of the bean property of this additional metadata. + */ + public XmlBeanPropertyMeta(BeanPropertyMeta<T> beanPropertyMeta) { + this.beanPropertyMeta = beanPropertyMeta; + + if (beanPropertyMeta.getField() != null) + findXmlInfo(beanPropertyMeta.getField().getAnnotation(Xml.class)); + if (beanPropertyMeta.getGetter() != null) + findXmlInfo(beanPropertyMeta.getGetter().getAnnotation(Xml.class)); + if (beanPropertyMeta.getSetter() != null) + findXmlInfo(beanPropertyMeta.getSetter().getAnnotation(Xml.class)); + + if (namespace == null) + namespace = beanPropertyMeta.getBeanMeta().getClassMeta().getXmlMeta().getNamespace(); + + if (beanPropertyMeta.isBeanUri() && xmlFormat != XmlFormat.ELEMENT) + xmlFormat = XmlFormat.ATTR; + } + + /** + * Returns the XML namespace associated with this bean property. + * <p> + * Namespace is determined in the following order: + * <ol> + * <li>{@link Xml#prefix()} annotation defined on bean property field. + * <li>{@link Xml#prefix()} annotation defined on bean getter. + * <li>{@link Xml#prefix()} annotation defined on bean setter. + * <li>{@link Xml#prefix()} annotation defined on bean. + * <li>{@link Xml#prefix()} annotation defined on bean package. + * <li>{@link Xml#prefix()} annotation defined on bean superclasses. + * <li>{@link Xml#prefix()} annotation defined on bean superclass packages. + * <li>{@link Xml#prefix()} annotation defined on bean interfaces. + * <li>{@link Xml#prefix()} annotation defined on bean interface packages. + * </ol> + * + * @return The namespace associated with this bean property, or <jk>null</jk> if no namespace is + * associated with it. + */ + public Namespace getNamespace() { + return namespace; + } + + /** + * Returns the XML format of this property from the {@link Xml#format} annotation on this bean property. + * + * @return The XML format, or {@link XmlFormat#NORMAL} if annotation not specified. + */ + protected XmlFormat getXmlFormat() { + return xmlFormat; + } + + /** + * Returns the XML content handler of this property from the {@link Xml#contentHandler} annotation on this bean property. + * + * @return The XML content handler, or <jk>null</jk> if annotation not specified. + */ + protected XmlContentHandler<T> getXmlContentHandler() { + return xmlContentHandler; + } + + /** + * Returns the child element of this property from the {@link Xml#childName} annotation on this bean property. + * + * @return The child element, or <jk>null</jk> if annotation not specified. + */ + protected String getChildName() { + return childName; + } + + /** + * Returns the bean property metadata that this metadata belongs to. + * + * @return The bean property metadata. Never <jk>null</jk>. + */ + protected BeanPropertyMeta<T> getBeanPropertyMeta() { + return beanPropertyMeta; + } + + @SuppressWarnings("unchecked") + private void findXmlInfo(Xml xml) { + if (xml == null) + return; + ClassMeta<?> cmProperty = beanPropertyMeta.getClassMeta(); + ClassMeta<?> cmBean = beanPropertyMeta.getBeanMeta().getClassMeta(); + String name = beanPropertyMeta.getName(); + if (! xml.name().isEmpty()) + throw new BeanRuntimeException(cmBean.getInnerClass(), "Annotation error on property ''{0}''. Found @Xml.name annotation can only be specified on types.", name); + + List<Xml> xmls = beanPropertyMeta.findAnnotations(Xml.class); + List<XmlSchema> schemas = beanPropertyMeta.findAnnotations(XmlSchema.class); + namespace = XmlUtils.findNamespace(xmls, schemas); + + if (xmlFormat == XmlFormat.NORMAL) + xmlFormat = xml.format(); + + boolean isCollection = cmProperty.isCollection() || cmProperty.isArray(); + + String cen = xml.childName(); + if ((! cen.isEmpty()) && (! isCollection)) + throw new BeanRuntimeException(cmProperty.getInnerClass(), "Annotation error on property ''{0}''. @Xml.childName can only be specified on collections and arrays.", name); + + if (xmlFormat == XmlFormat.COLLAPSED) { + if (isCollection) { + if (cen.isEmpty()) + cen = cmProperty.getXmlMeta().getChildName(); + if (cen == null || cen.isEmpty()) + cen = cmProperty.getElementType().getXmlMeta().getElementName(); + if (cen == null || cen.isEmpty()) + cen = name; + } else { + throw new BeanRuntimeException(cmBean.getInnerClass(), "Annotation error on property ''{0}''. @Xml.format=COLLAPSED can only be specified on collections and arrays.", name); + } + if (cen.isEmpty() && isCollection) + cen = cmProperty.getXmlMeta().getElementName(); + } + + try { + if (xmlFormat == XmlFormat.CONTENT && xml.contentHandler() != XmlContentHandler.NULL.class) + xmlContentHandler = (XmlContentHandler<T>) xml.contentHandler().newInstance(); + } catch (Exception e) { + throw new BeanRuntimeException(cmBean.getInnerClass(), "Could not instantiate content handler ''{0}''", xml.contentHandler().getName()).initCause(e); + } + + if (! cen.isEmpty()) + childName = cen; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.class new file mode 100755 index 0000000..f5ffbb0 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.java new file mode 100755 index 0000000..4252bab --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlClassMeta.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import java.util.*; + +import com.ibm.juno.core.utils.*; +import com.ibm.juno.core.xml.annotation.*; + + +/** + * Metadata on classes specific to the XML serializers and parsers pulled from the {@link Xml @Xml} annotation on the class. + * + * @author James Bognar ([email protected]) + */ +public class XmlClassMeta { + + private final Namespace namespace; + private final Xml xml; + private final XmlFormat format; + private final String elementName; + private final String childName; + + /** + * Constructor. + * + * @param c The class that this annotation is defined on. + */ + public XmlClassMeta(Class<?> c) { + this.namespace = findNamespace(c); + this.xml = ReflectionUtils.getAnnotation(Xml.class, c); + if (xml != null) { + this.format = xml.format(); + this.elementName = StringUtils.nullIfEmpty(xml.name()); + this.childName = StringUtils.nullIfEmpty(xml.childName()); + + } else { + this.format = XmlFormat.NORMAL; + this.elementName = null; + this.childName = null; + } + } + + /** + * Returns the {@link Xml} annotation defined on the class. + * + * @return The value of the {@link Xml} annotation defined on the class, or <jk>null</jk> if annotation is not specified. + */ + protected Xml getAnnotation() { + return xml; + } + + /** + * Returns the {@link Xml#format()} annotation defined on the class. + * + * @return The value of the {@link Xml#format()} annotation, or {@link XmlFormat#NORMAL} if not specified. + */ + protected XmlFormat getFormat() { + return format; + } + + /** + * Returns the {@link Xml#name()} annotation defined on the class. + * + * @return The value of the {@link Xml#name()} annotation, or <jk>null</jk> if not specified. + */ + protected String getElementName() { + return elementName; + } + + /** + * Returns the {@link Xml#childName()} annotation defined on the class. + * + * @return The value of the {@link Xml#childName()} annotation, or <jk>null</jk> if not specified. + */ + protected String getChildName() { + return childName; + } + + /** + * Returns the XML namespace associated with this class. + * <p> + * Namespace is determined in the following order: + * <ol> + * <li>{@link Xml#prefix()} annotation defined on class. + * <li>{@link Xml#prefix()} annotation defined on package. + * <li>{@link Xml#prefix()} annotation defined on superclasses. + * <li>{@link Xml#prefix()} annotation defined on superclass packages. + * <li>{@link Xml#prefix()} annotation defined on interfaces. + * <li>{@link Xml#prefix()} annotation defined on interface packages. + * </ol> + * + * @return The namespace associated with this class, or <jk>null</jk> if no namespace is + * associated with it. + */ + protected Namespace getNamespace() { + return namespace; + } + + private Namespace findNamespace(Class<?> c) { + if (c == null) + return null; + + List<Xml> xmls = ReflectionUtils.findAnnotations(Xml.class, c); + List<XmlSchema> schemas = ReflectionUtils.findAnnotations(XmlSchema.class, c); + return XmlUtils.findNamespace(xmls, schemas); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler$NULL.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler$NULL.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler$NULL.class new file mode 100755 index 0000000..cd9f86e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler$NULL.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.class new file mode 100755 index 0000000..b0f1b7a Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.java new file mode 100755 index 0000000..3ebfef3 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlContentHandler.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import javax.xml.stream.*; + +import com.ibm.juno.core.dto.atom.*; +import com.ibm.juno.core.xml.annotation.*; + +/** + * Customization class that allows a bean (or parts of a bean) to be serialized as XML text or mixed content. + * <p> + * For example, the ATOM specification allows text elements (e.g. title, subtitle...) + * to be either plain text or XML depending on the value of a <xa>type</xa> attribute. + * The behavior of text escaping thus depends on that attribute. + * + * <p class='bcode'> + * <xt><feed</xt> <xa>xmlns</xa>=<xs>"http://www.w3.org/2005/Atom"</xs><xt>></xt> + * <xt><title</xt> <xa>type</xa>=<xs>"html"</xs><xt>></xt> + * &lt;p&gt;&lt;i&gt;This is the title&lt;/i&gt;&lt;/p&gt; + * <xt></title></xt> + * <xt><title</xt> <xa>type</xa>=<xs>"xhtml"</xs><xt>></xt> + * <xt><div</xt> <xa>xmlns</xa>=<xs>"http://www.w3.org/1999/xhtml"</xs><xt>></xt> + * <xt><p><i></xt>This is the subtitle<xt></i></p></xt> + * <xt></div></xt> + * <xt></title></xt> + * <xt></feed></xt> + * </p> + * + * <p> + * The ATOM {@link Text} class (the implementation for both the <xt><title></xt> and <xt><subtitle></xt> + * tags shown above) then associates a content handler through the {@link Xml#contentHandler()} annotation + * on the bean property containing the text, like so... + * + * <p class='bcode'> + * <ja>@Xml</ja>(format=<jsf>ATTR</jsf>) + * <jk>public</jk> String getType() { + * <jk>return</jk> <jf>type</jf>; + * } + * + * <ja>@Xml</ja>(format=<jsf>CONTENT</jsf>, contentHandler=TextContentHandler.<jk>class</jk>) + * <jk>public</jk> String getText() { + * <jk>return</jk> <jf>text</jf>; + * } + * + * <jk>public void</jk> setText(String text) { + * <jk>this</jk>.<jf>text</jf> = text; + * } + * </p> + * + * <p> + * The content handler that transforms the output is shown below... + * + * <p class='bcode'> + * <jk>public static class</jk> TextContentHandler <jk>implements</jk> XmlContentHandler<Text> { + * + * <ja>@Override</ja> + * <jk>public void</jk> parse(XMLStreamReader r, Text text) <jk>throws</jk> Exception { + * String type = text.<jf>type</jf>; + * <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) + * text.<jf>text</jf> = <jsm>decode</jsm>(readXmlContents(r).trim()); + * <jk>else</jk> + * text.<jf>text</jf> = <jsm>decode</jsm>(r.getElementText().trim()); + * } + * + * <ja>@Override</ja> + * <jk>public void</jk> serialize(XmlSerializerWriter w, Text text) <jk>throws</jk> Exception { + * String type = text.<jf>type</jf>; + * String content = text.<jf>text</jf>; + * <jk>if</jk> (type != <jk>null</jk> && type.equals(<js>"xhtml"</js>)) + * w.encodeTextInvalidChars(content); + * <jk>else</jk> + * w.encodeText(content); + * } + * } + * </p> + * + * <h6 class='topic'>Notes</h6> + * <ul> + * <li>The {@link Xml#contentHandler()} annotation can only be specified on a bean class, or a bean property + * of format {@link XmlFormat#CONTENT}. + * </ul> + * + * + * @author James Bognar ([email protected]) + * @param <T> The class type of the bean + */ +public interface XmlContentHandler<T> { + + /** + * Represents <jk>null</jk> on the {@link Xml#contentHandler()} annotation. + */ + public static interface NULL extends XmlContentHandler<Object> {} + + /** + * Reads XML element content the specified reader and sets the appropriate value on the specified bean. + * <p> + * When this method is called, the attributes have already been parsed and set on the bean. + * Therefore, if the content handling is different based on some XML attribute (e.g. + * <code><xa>type</xa>=<xs>"text/xml"</xs></code> vs <code><xa>type</xa>=<xs>"text/plain"</xs></code>) + * then that attribute value can be obtained via the set bean property. + * + * @param r The XML stream reader. + * When called, the reader is positioned on the element containing the text to read. + * For example, calling <code>r.getElementText()</code> can be called immediately + * to return the element text if the element contains only characters and whitespace. + * However typically, the stream is going to contain XML elements that need to + * be handled special (otherwise you wouldn't need to use an <code>XmlContentHandler</code> + * to begin with). + * @param bean The bean where the parsed contents are going to be placed. + * Subclasses determine how the content maps to values in the bean. + * However, typically the contents map to a single property on the bean. + * @throws Exception If any problem occurs. Causes parse to fail. + */ + public void parse(XMLStreamReader r, T bean) throws Exception; + + /** + * Writes XML element content from values in the specified bean. + * + * @param w The XML output writer. + * When called, the XML element/attributes and + * whitespace/indentation (if enabled) have already been written to the stream. + * Subclasses must simply write the contents of the element. + * @param bean The bean whose values will be converted to XML content. + * @throws Exception If any problems occur. Causes serialize to fail. + */ + public void serialize(XmlSerializerWriter w, T bean) throws Exception; + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer$Simple.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer$Simple.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer$Simple.class new file mode 100755 index 0000000..876d0e7 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer$Simple.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.class new file mode 100755 index 0000000..7eef3a8 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.java new file mode 100755 index 0000000..a2d7da0 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlDocSerializer.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import static com.ibm.juno.core.xml.XmlSerializerProperties.*; + +import java.io.*; + +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.serializer.*; + +/** + * Serializes POJOs to HTTP responses as XML. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/xml</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/xml</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Same as {@link XmlSerializer}, except prepends <code><xt><?xml</xt> <xa>version</xa>=<xs>'1.0'</xs> <xa>encoding</xa>=<xs>'UTF-8'</xs><xt>?></xt></code> to the response + * to make it a valid XML document. + * + * + * @author James Bognar ([email protected]) + */ +public class XmlDocSerializer extends XmlSerializer { + + /** Default serializer without namespaces. */ + @Produces(value="text/xml+simple",contentType="text/xml") + public static class Simple extends XmlDocSerializer { + /** Constructor */ + public Simple() { + setProperty(XML_enableNamespaces, false); + } + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + protected void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException { + XmlSerializerContext xctx = (XmlSerializerContext)ctx; + XmlSerializerWriter w = xctx.getWriter(out); + w.append("<?xml") + .attr("version", "1.0") + .attr("encoding", "UTF-8") + .appendln("?>"); + super.doSerialize(o, w, ctx); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.class new file mode 100755 index 0000000..8ef1946 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.class differ http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.java new file mode 100755 index 0000000..9503b03 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParser.java @@ -0,0 +1,558 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * The source code for this program is not published or otherwise + * divested of its trade secrets, irrespective of what has been + * deposited with the U.S. Copyright Office. + *******************************************************************************/ +package com.ibm.juno.core.xml; + +import static com.ibm.juno.core.utils.StringUtils.*; +import static com.ibm.juno.core.xml.XmlUtils.*; +import static com.ibm.juno.core.xml.annotation.XmlFormat.*; +import static javax.xml.stream.XMLStreamConstants.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.xml.stream.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.xml.annotation.*; + +/** + * Parses text generated by the {@link XmlSerializer} class back into a POJO model. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Content-Type</code> types: <code>text/xml</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * See the {@link XmlSerializer} class for a description of Juno-generated XML. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link XmlParserProperties} + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +@Consumes({"text/xml","application/xml"}) +public class XmlParser extends ReaderParser { + + /** Default parser, all default settings.*/ + public static final XmlParser DEFAULT = new XmlParser().lock(); + + private static final int UNKNOWN=0, OBJECT=1, ARRAY=2, STRING=3, NUMBER=4, BOOLEAN=5, NULL=6; + + + /** XML specific properties currently defined on this class */ + protected transient XmlParserProperties xpp = new XmlParserProperties(); + + + private <T> T parseAnything(ClassMeta<T> nt, XmlParserContext ctx, String currAttr, XMLStreamReader r, BeanPropertyMeta p, Object outer, boolean isRoot) throws ParseException, IOException { + + try { + BeanContext bc = ctx.getBeanContext(); + if (nt == null) + nt = (ClassMeta<T>)object(); + PojoFilter<T,Object> filter = (PojoFilter<T,Object>)nt.getPojoFilter(); + ClassMeta<?> ft = nt.getFilteredClassMeta(); + + String wrapperAttr = (isRoot && ctx.isPreserveRootElement()) ? r.getName().getLocalPart() : null; + String typeAttr = r.getAttributeValue(null, "type"); + int jsonType = getJsonType(typeAttr); + String b = r.getAttributeValue(ctx.getXsiNs(), "nil"); + if (b == null) + b = r.getAttributeValue(null, "nil"); + boolean isNull = b != null && b.equals("true"); + if (jsonType == 0) { + try { + String elementName = decode(r.getLocalName()); + if (elementName == null || elementName.equals(currAttr)) + jsonType = UNKNOWN; + else + jsonType = getJsonType(elementName); + } catch (Exception e) { + throw e; + } + } + if (! ft.canCreateNewInstance(outer)) { + String c = r.getAttributeValue(null, "_class"); + if (c != null) { + ft = nt = (ClassMeta<T>)bc.getClassMetaFromString(c); + } + } + Object o = null; + + if (jsonType == NULL) { + r.nextTag(); // Discard end tag + return null; + } + if (isNull) { + while (true) { + int e = r.next(); + if (e == END_ELEMENT) + return null; + } + } + + if (ft.isObject()) { + if (jsonType == OBJECT) { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + if (wrapperAttr != null) + m = new ObjectMap(bc).append(wrapperAttr, m); + o = m.cast(); + } else if (jsonType == ARRAY) + o = parseIntoCollection(ctx, r, new ObjectList(bc), object()); + else if (jsonType == STRING) { + o = getText(ctx, r); + if (ft.isChar()) + o = o.toString().charAt(0); + } + else if (jsonType == NUMBER) + o = parseNumber(getText(ctx, r), null); + else if (jsonType == BOOLEAN) + o = Boolean.parseBoolean(getText(ctx, r)); + else if (jsonType == UNKNOWN) + o = getUnknown(ctx, r); + } else if (ft.isBoolean()) { + o = Boolean.parseBoolean(getText(ctx, r)); + } else if (ft.isCharSequence()) { + o = getText(ctx, r); + } else if (ft.isChar()) { + o = getText(ctx, r).charAt(0); + } else if (ft.isMap()) { + Map m = (ft.canCreateNewInstance(outer) ? (Map)ft.newInstance(outer) : new ObjectMap(bc)); + o = parseIntoMap(ctx, r, m, ft.getKeyType(), ft.getValueType()); + if (wrapperAttr != null) + o = new ObjectMap(bc).append(wrapperAttr, m); + } else if (ft.isCollection()) { + Collection l = (ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance(outer) : new ObjectList(bc)); + o = parseIntoCollection(ctx, r, l, ft.getElementType()); + } else if (ft.isNumber()) { + o = parseNumber(getText(ctx, r), (Class<? extends Number>)ft.getInnerClass()); + } else if (ft.canCreateNewInstanceFromObjectMap(outer)) { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + o = ft.newInstanceFromObjectMap(outer, m); + } else if (ft.canCreateNewBean(outer)) { + if (ft.getXmlMeta().getFormat() == XmlFormat.COLLAPSED) { + String fieldName = r.getLocalName(); + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + BeanPropertyMeta bpm = m.getMeta().getXmlMeta().getPropertyMeta(fieldName); + bpm.set(m, parseAnything(m.getMeta().getClassMeta(), ctx, currAttr, r, p, m.getBean(false), false)); + o = m.getBean(); + } else { + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + o = parseIntoBean(ctx, r, m).getBean(); + } + } else if (ft.isArray()) { + ArrayList l = (ArrayList)parseIntoCollection(ctx, r, new ArrayList(), ft.getElementType()); + o = bc.toArray(ft, l); + } else if (ft.canCreateNewInstanceFromString(outer)) { + o = ft.newInstanceFromString(outer, getText(ctx, r)); + } else { + throw new ParseException("Class ''{0}'' could not be instantiated. Reason: ''{1}''", ft.getInnerClass().getName(), ft.getNotABeanReason()); + } + + if (filter != null && o != null) + o = filter.unfilter(o, nt); + + if (outer != null) + setParent(nt, o, outer); + + if (currAttr != null) + setName(nt, o, currAttr); + + return (T)o; + } catch (ParseException e) { + throw e; + } catch (IOException e) { + throw e; + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + throw new ParseException("Error occurred while trying to parse class type ''{0}''", nt).initCause(e); + } + } + + private <K,V> Map<K,V> parseIntoMap(XmlParserContext ctx, XMLStreamReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) throws ParseException, IOException { + BeanContext bc = ctx.getBeanContext(); + try { + int depth = 0; + for (int i = 0; i < r.getAttributeCount(); i++) { + String a = r.getAttributeLocalName(i); + // TODO - Need better handling of namespaces here. + if (! (a.equals("type"))) { + K key = convertAttrToType(m, a, keyType); + V value = convertAttrToType(m, r.getAttributeValue(i), valueType); + m.put(key, value); + } + } + do { + int event = r.nextTag(); + String currAttr; + if (event == START_ELEMENT) { + depth++; + currAttr = decode(r.getLocalName()); + K key = convertAttrToType(m, currAttr, keyType); + V value = parseAnything(valueType, ctx, currAttr, r, null, m, false); + if (valueType.isObject() && m.containsKey(key)) { + Object o = m.get(key); + if (o instanceof List) + ((List)o).add(value); + else + m.put(key, (V)new ObjectList(o, value).setBeanContext(bc)); + } else { + m.put(key, value); + } + } else if (event == END_ELEMENT) { + depth--; + return m; + } + } while (depth > 0); + return m; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + private <E> Collection<E> parseIntoCollection(XmlParserContext ctx, XMLStreamReader r, Collection<E> l, ClassMeta<E> elementType) throws ParseException, IOException { + try { + int depth = 0; + do { + int event = r.nextTag(); + if (event == START_ELEMENT) { + depth++; + E value = parseAnything(elementType, ctx, null, r, null, l, false); + l.add(value); + } else if (event == END_ELEMENT) { + depth--; + return l; + } + } while (depth > 0); + return l; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + private Object[] parseArgs(XmlParserContext ctx, XMLStreamReader r, ClassMeta<?>[] argTypes) throws ParseException, IOException { + try { + int depth = 0; + Object[] o = new Object[argTypes.length]; + int i = 0; + do { + int event = r.nextTag(); + if (event == START_ELEMENT) { + depth++; + o[i] = parseAnything(argTypes[i], ctx, null, r, null, null, false); + i++; + } else if (event == END_ELEMENT) { + depth--; + return o; + } + } while (depth > 0); + return o; + } catch (XMLStreamException e) { + throw new ParseException(e); + } + } + + private int getJsonType(String s) { + if (s == null) + return UNKNOWN; + char c = s.charAt(0); + switch(c) { + case 'o': return (s.equals("object") ? OBJECT : UNKNOWN); + case 'a': return (s.equals("array") ? ARRAY : UNKNOWN); + case 's': return (s.equals("string") ? STRING : UNKNOWN); + case 'b': return (s.equals("boolean") ? BOOLEAN : UNKNOWN); + case 'n': { + c = s.charAt(2); + switch(c) { + case 'm': return (s.equals("number") ? NUMBER : UNKNOWN); + case 'l': return (s.equals("null") ? NULL : UNKNOWN); + } + //return NUMBER; + } + } + return UNKNOWN; + } + + private <T> BeanMap<T> parseIntoBean(XmlParserContext ctx, XMLStreamReader r, BeanMap<T> m) throws Exception { + BeanMeta bMeta = m.getMeta(); + XmlBeanMeta xmlMeta = bMeta.getXmlMeta(); + + for (int i = 0; i < r.getAttributeCount(); i++) { + String key = decode(r.getAttributeLocalName(i)); + String val = r.getAttributeValue(i); + BeanPropertyMeta bpm = xmlMeta.getPropertyMeta(key); + if (bpm == null) { + if (m.getMeta().isSubTyped()) { + m.put(key, val); + } else { + Location l = r.getLocation(); + onUnknownProperty(ctx, key, m, l.getLineNumber(), l.getColumnNumber()); + } + } else { + bpm.set(m, val); + } + } + + BeanPropertyMeta cp = xmlMeta.getXmlContentProperty(); + if (cp != null) { + XmlContentHandler h = xmlMeta.getXmlContentHandler(); + if (h != null) { + h.parse(r, m.getBean()); + } else { + String text = r.getElementText(); + cp.set(m, decode(text.trim())); + } + return m; + } + + int depth = 0; + do { + int event = r.nextTag(); + String currAttr; + if (event == START_ELEMENT) { + depth++; + currAttr = decode(r.getLocalName()); + BeanPropertyMeta pMeta = xmlMeta.getPropertyMeta(currAttr); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + m.put(currAttr, parseAnything(ctx.getBeanContext().string(), ctx, currAttr, r, null, m.getBean(false), false)); + } else { + Location l = r.getLocation(); + onUnknownProperty(ctx, currAttr, m, l.getLineNumber(), l.getColumnNumber()); + skipCurrentTag(r); + } + } else { + XmlFormat xf = pMeta.getXmlMeta().getXmlFormat(); + if (xf == COLLAPSED) { + pMeta.add(m, parseAnything(pMeta.getClassMeta().getElementType(), ctx, currAttr, r, null, m.getBean(false), false)); + } else if (xf == ATTR) { + pMeta.set(m, decode(r.getAttributeValue(0))); + r.nextTag(); + } else { + pMeta.set(m, parseAnything(pMeta.getClassMeta(), ctx, currAttr, r, pMeta, m.getBean(false), false)); + } + } + } else if (event == END_ELEMENT) { + depth--; + return m; + } + } while (depth > 0); + return m; + } + + private void skipCurrentTag(XMLStreamReader r) throws XMLStreamException { + int depth = 1; + do { + int event = r.next(); + if (event == START_ELEMENT) + depth++; + else if (event == END_ELEMENT) + depth--; + } while (depth > 0); + } + + private String getText(XmlParserContext ctx, XMLStreamReader r) throws Exception { + String s = r.getElementText(); + if (s.length() == 0) + return s; + if (ctx.isTrimWhitespace() && Character.isWhitespace(s.charAt(0)) || Character.isWhitespace(s.charAt(s.length()-1))) + s = s.trim(); + if (s.indexOf('_') == -1) + return s; + return decode(s); + } + + private Object getUnknown(XmlParserContext ctx, XMLStreamReader r) throws Exception { + BeanContext bc = ctx.getBeanContext(); + if (r.getEventType() != XMLStreamConstants.START_ELEMENT) { + throw new XMLStreamException("parser must be on START_ELEMENT to read next text", r.getLocation()); + } + ObjectMap m = null; + + // If this element has attributes, then it's always an ObjectMap. + if (r.getAttributeCount() > 0) { + m = new ObjectMap(bc); + for (int i = 0; i < r.getAttributeCount(); i++) { + String key = decode(r.getAttributeLocalName(i)); + String val = r.getAttributeValue(i); + if (! key.equals("type")) + m.put(key, val); + } + } + int eventType = r.next(); + StringBuilder sb = new StringBuilder(); + while (eventType != XMLStreamConstants.END_ELEMENT) { + if (eventType == XMLStreamConstants.CHARACTERS || eventType == XMLStreamConstants.CDATA || eventType == XMLStreamConstants.SPACE || eventType == XMLStreamConstants.ENTITY_REFERENCE) { + sb.append(r.getText()); + } else if (eventType == XMLStreamConstants.PROCESSING_INSTRUCTION || eventType == XMLStreamConstants.COMMENT) { + // skipping + } else if (eventType == XMLStreamConstants.END_DOCUMENT) { + throw new XMLStreamException("Unexpected end of document when reading element text content", r.getLocation()); + } else if (eventType == XMLStreamConstants.START_ELEMENT) { + // Oops...this has an element in it. + // Parse it as a map. + if (m == null) + m = new ObjectMap(bc); + int depth = 0; + do { + int event = (eventType == -1 ? r.nextTag() : eventType); + String currAttr; + if (event == START_ELEMENT) { + depth++; + currAttr = decode(r.getLocalName()); + String key = convertAttrToType(null, currAttr, string()); + Object value = parseAnything(object(), ctx, currAttr, r, null, null, false); + if (m.containsKey(key)) { + Object o = m.get(key); + if (o instanceof ObjectList) + ((ObjectList)o).add(value); + else + m.put(key, new ObjectList(o, value).setBeanContext(bc)); + } else { + m.put(key, value); + } + + } else if (event == END_ELEMENT) { + depth--; + break; + } + eventType = -1; + } while (depth > 0); + break; + } else { + throw new XMLStreamException("Unexpected event type " + eventType, r.getLocation()); + } + eventType = r.next(); + } + String s = sb.toString(); + if (s.length() > 0) { + if (ctx.isTrimWhitespace() && Character.isWhitespace(s.charAt(0)) || Character.isWhitespace(s.charAt(s.length()-1))) + s = s.trim(); + if (s.indexOf('_') != -1) + s = decode(s); + } + if (m != null) { + if (! s.isEmpty()) + m.put("contents", s); + return m; + } + return s; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Parser */ + public XmlParserContext createContext(ObjectMap properties, Method javaMethod, Object outer) { + return new XmlParserContext(getBeanContext(), pp, xpp, properties, javaMethod, outer); + } + + @Override /* Parser */ + protected <T> T doParse(Reader in, int estimatedSize, ClassMeta<T> type, ParserContext ctx) throws ParseException, IOException { + type = ctx.getBeanContext().normalizeClassMeta(type); + XmlParserContext xctx = (XmlParserContext)ctx; + return parseAnything(type, xctx, null, xctx.getReader(in, estimatedSize), null, ctx.getOuter(), true); + } + + @Override /* ReaderParser */ + protected <K,V> Map<K,V> doParseIntoMap(Reader in, int estimatedSize, Map<K,V> m, Type keyType, Type valueType, ParserContext ctx) throws ParseException, IOException { + ClassMeta cm = ctx.getBeanContext().getMapClassMeta(m.getClass(), keyType, valueType); + XmlParserContext xctx = (XmlParserContext)ctx; + return parseIntoMap(xctx, xctx.getReader(in, estimatedSize), m, cm.getKeyType(), cm.getValueType()); + } + + @Override /* ReaderParser */ + protected <E> Collection<E> doParseIntoCollection(Reader in, int estimatedSize, Collection<E> c, Type elementType, ParserContext ctx) throws ParseException, IOException { + ClassMeta cm = ctx.getBeanContext().getCollectionClassMeta(c.getClass(), elementType); + XmlParserContext xctx = (XmlParserContext)ctx; + return parseIntoCollection(xctx, xctx.getReader(in, estimatedSize), c, cm.getElementType()); + } + + @Override /* ReaderParser */ + protected Object[] doParseArgs(Reader in, int estimatedSize, ClassMeta<?>[] argTypes, ParserContext ctx) throws ParseException, IOException { + XmlParserContext xctx = (XmlParserContext)ctx; + return parseArgs(xctx, xctx.getReader(in, estimatedSize), argTypes); + } + + @Override /* CoreApi */ + public XmlParser setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! xpp.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public XmlParser setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public XmlParser addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public XmlParser addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> XmlParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public XmlParser setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public XmlParser lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public XmlParser clone() { + try { + XmlParser c = (XmlParser)super.clone(); + c.xpp = xpp.clone(); + return c; + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Shouldn't happen. + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParserContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParserContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParserContext.class new file mode 100755 index 0000000..7b0a2a3 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/xml/XmlParserContext.class differ
