http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java new file mode 100755 index 0000000..669a832 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerProperties.java @@ -0,0 +1,303 @@ +/******************************************************************************* + * 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.serializer; + +import com.ibm.juno.core.*; + +/** + * Configurable properties common to all {@link Serializer} classes. + * <p> + * Use the {@link Serializer#setProperty(String, Object)} method to set property values. + * + * @author James Bognar ([email protected]) + */ +public final class SerializerProperties implements Cloneable { + + /** + * Max depth ({@link Integer}, default=<code>100</code>). + * <p> + * Abort serialization if specified depth is reached in the POJO tree. + * If this depth is exceeded, an exception is thrown. + * This prevents stack overflows from occurring when trying to serialize models with recursive references. + */ + public static final String SERIALIZER_maxDepth = "Serializer.maxDepth"; + + /** + * Initial depth ({@link Integer}, default=<code>0</code>). + * <p> + * The initial indentation level at the root. + * Useful when constructing document fragments that need to be indented at a certain level. + */ + public static final String SERIALIZER_initialDepth = "Serializer.initialDepth"; + + /** + * Automatically detect POJO recursions ({@link Boolean}, default=<jk>false</jk>). + * <p> + * Specifies that recursions should be checked for during serialization. + * <p> + * Recursions can occur when serializing models that aren't true trees, but rather contain loops. + * <p> + * The behavior when recursions are detected depends on the value for {@link #SERIALIZER_ignoreRecursions}. + * <p> + * For example, if a model contains the links A->B->C->A, then the JSON generated will look like + * the following when <jsf>SERIALIZER_ignoreRecursions</jsf> is <jk>true</jk>... + * <code>{A:{B:{C:null}}}</code><br> + * <p> + * Note: Checking for recursion can cause a small performance penalty. + */ + public static final String SERIALIZER_detectRecursions = "Serializer.detectRecursions"; + + /** + * Ignore recursion errors ({@link Boolean}, default=<jk>false</jk>). + * <p> + * Used in conjunction with {@link #SERIALIZER_detectRecursions}. + * Setting is ignored if <jsf>SERIALIZER_detectRecursions</jsf> is <jk>false</jk>. + * <p> + * If <jk>true</jk>, when we encounter the same object when serializing a tree, + * we set the value to <jk>null</jk>. + * Otherwise, an exception is thrown. + */ + public static final String SERIALIZER_ignoreRecursions = "Serializer.ignoreRecursions"; + + /** + * Debug mode ({@link Boolean}, default=<jk>false</jk>). + * <p> + * Enables the following additional information during serialization: + * <ul> + * <li>When bean getters throws exceptions, the exception includes the object stack information + * in order to determine how that method was invoked. + * <li>Enables {#link SERIALIZER_detectRecursions}. + * </ul> + */ + public static final String SERIALIZER_debug = "Serializer.debug"; + + /** + * Use indentation in output ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, newlines and indentation is added to the output to improve readability. + */ + public static final String SERIALIZER_useIndentation = "Serializer.useIndentation"; + + /** + * Add class attributes to output ({@link Boolean}, default=<jk>false</jk>). + * <p> + * If <jk>true</jk>, then <js>"_class"</js> attributes will be added to beans if their type cannot be inferred through reflection. + * This is used to recreate the correct objects during parsing if the object types cannot be inferred. + * For example, when serializing a {@code Map<String,Object>} field, where the bean class cannot be determined from the value type. + */ + public static final String SERIALIZER_addClassAttrs = "Serializer.addClassAttrs"; + + /** + * Quote character ({@link Character}, default=<js>'"'</js>. + * <p> + * This is the character used for quoting attributes and values. + */ + public static final String SERIALIZER_quoteChar = "Serializer.quoteChar"; + + /** + * Boolean. Trim null bean property values from output. + * <p> + * If <jk>true</jk>, null bean values will not be serialized to the output. + * <p> + * Note that enabling this setting has the following effects on parsing: + * <ul> + * <li>Map entries with <jk>null</jk> values will be lost. + * </ul> + * <p> + * Default is <jk>true</jk>. + */ + public static final String SERIALIZER_trimNullProperties = "Serializer.trimNullProperties"; + + /** + * Boolean. Trim empty lists and arrays from output. + * <p> + * If <jk>true</jk>, empty list values will not be serialized to the output. + * <p> + * Note that enabling this setting has the following effects on parsing: + * <ul> + * <li>Map entries with empty list values will be lost. + * <li>Bean properties with empty list values will not be set. + * </ul> + * <p> + * Default is <jk>false</jk>. + */ + public static final String SERIALIZER_trimEmptyLists = "Serializer.trimEmptyLists"; + + /** + * Boolean. Trim empty maps from output. + * <p> + * If <jk>true</jk>, empty map values will not be serialized to the output. + * <p> + * Note that enabling this setting has the following effects on parsing: + * <ul> + * <li>Bean properties with empty map values will not be set. + * </ul> + * <p> + * Default is <jk>false</jk>. + */ + public static final String SERIALIZER_trimEmptyMaps = "Serializer.trimEmptyMaps"; + + /** + * String. URI base for relative URIs. + * <p> + * Prepended to relative URIs during serialization (along with the {@link #SERIALIZER_absolutePathUriBase} if specified. + * (i.e. URIs not containing a schema and not starting with <js>'/'</js>). + * (e.g. <js>"foo/bar"</js>) + * <p> + * Default is <js>""</js>. + * + * <dl> + * <dt>Examples:</dt> + * <dd> + * <table class='styled'> + * <tr><th>SERIALIZER_relativeUriBase</th><th>URI</th><th>Serialized URI</th></tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>mywebapp</code></td> + * <td><code>http://foo:9080/bar/baz/mywebapp</code></td> + * </tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>/mywebapp</code></td> + * <td><code>/mywebapp</code></td> + * </tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>http://mywebapp</code></td> + * <td><code>http://mywebapp</code></td> + * </tr> + * </table> + * </dd> + * </dl> + */ + public static final String SERIALIZER_relativeUriBase = "Serializer.relativeUriBase"; + + /** + * Boolean. Sort arrays and collections alphabetically before serializing. + * <p> + * Note that this introduces a performance penalty. + * <p> + * Default is <jk>false</jk>. + */ + public static final String SERIALIZER_sortCollections = "Serializer.sortCollections"; + + /** + * Boolean. Sort maps alphabetically before serializing. + * <p> + * Note that this introduces a performance penalty. + * <p> + * Default is <jk>false</jk>. + */ + public static final String SERIALIZER_sortMaps = "Serializer.sortMaps"; + + /** + * String. URI base for relative URIs with absolute paths. + * <p> + * Prepended to relative absolute-path URIs during serialization. + * (i.e. URIs starting with <js>'/'</js>). + * (e.g. <js>"/foo/bar"</js>) + * <p> + * Default is <js>""</js>. + * + * <dl> + * <dt>Examples:</dt> + * <dd> + * <table class='styled'> + * <tr><th>SERIALIZER_absolutePathUriBase</th><th>URI</th><th>Serialized URI</th></tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>mywebapp</code></td> + * <td><code>mywebapp</code></td> + * </tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>/mywebapp</code></td> + * <td><code>http://foo:9080/bar/baz/mywebapp</code></td> + * </tr> + * <tr> + * <td><code>http://foo:9080/bar/baz</code></td> + * <td><code>http://mywebapp</code></td> + * <td><code>http://mywebapp</code></td> + * </tr> + * </table> + * </dd> + * </dl> + */ + public static final String SERIALIZER_absolutePathUriBase = "Serializer.absolutePathUriBase"; + + int maxDepth = 100, initialDepth = 0; + boolean + debug = false, + detectRecursions = false, + ignoreRecursions = false, + useIndentation = false, + addClassAttrs = false, + trimNulls = true, + trimEmptyLists = false, + trimEmptyMaps = false, + sortCollections = false, + sortMaps = false; + char quoteChar = '"'; + String relativeUriBase="", absolutePathUriBase=""; + + /** + * Sets the specified property value. + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + */ + public boolean setProperty(String property, Object value) { + BeanContext bc = BeanContext.DEFAULT; + if (property.equals(SERIALIZER_maxDepth)) + maxDepth = bc.convertToType(value, Integer.class); + else if (property.equals(SERIALIZER_debug)) + debug = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_detectRecursions)) + detectRecursions = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_ignoreRecursions)) + ignoreRecursions = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_useIndentation)) + useIndentation = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_addClassAttrs)) + addClassAttrs = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_quoteChar)) + quoteChar = bc.convertToType(value, Character.class); + else if (property.equals(SERIALIZER_trimNullProperties)) + trimNulls = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_trimEmptyLists)) + trimEmptyLists = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_trimEmptyMaps)) + trimEmptyMaps = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_relativeUriBase)) + relativeUriBase = value == null ? null : value.toString(); + else if (property.equals(SERIALIZER_absolutePathUriBase)) + absolutePathUriBase = value == null ? null : value.toString(); + else if (property.equals(SERIALIZER_sortCollections)) + sortCollections = bc.convertToType(value, Boolean.class); + else if (property.equals(SERIALIZER_sortMaps)) + sortMaps = bc.convertToType(value, Boolean.class); + else + return false; + return true; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Cloneable */ + public SerializerProperties clone() { + try { + return (SerializerProperties)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); // Won'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/serializer/SerializerWriter.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.class new file mode 100755 index 0000000..0bd3142 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.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/serializer/SerializerWriter.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java new file mode 100755 index 0000000..7a91e6a --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/SerializerWriter.java @@ -0,0 +1,308 @@ +/******************************************************************************* + * 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.serializer; + +import java.io.*; +import java.net.*; + +import com.ibm.juno.core.utils.*; + +/** + * Simple wrapper around a standard {@link Writer} with additional methods. + * <p> + * Modeled after the Java ProcessBuilder class so that you can chain commands to reduce + * the need for string concatenation for performance reasons. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * writer.append(<js>"foo"</js>).nl().i(5).append(<js>"bar"</js>); + * </p> + * + * @author James Bognar ([email protected]) + */ +public class SerializerWriter extends Writer { + + /** The underlying writer. */ + protected final Writer out; + + /** Use-indentation flag. */ + protected final boolean useIndentation; + + /** Use-whitespace flag. */ + protected final boolean useWhitespace; + + /** The quote character being used by this writer. */ + protected final char quoteChar; + + /** The base (e.g. <js>https://localhost:9443/contextPath"</js>) for relative URIs (e.g. <js>"my/path"</js>). */ + protected final String relativeUriBase; + + /** The base (e.g. <js>https://localhost:9443"</js>) for relative URIs with absolute paths (e.g. <js>"/contextPath/my/path"</js>). */ + protected final String absolutePathUriBase; + + /** + * @param out The writer being wrapped. + * @param useIndentation If <jk>true</jk>, calling {@link #cr(int)} will create an indentation. + * @param useWhitespace If <jk>true</jk>, calling {@link #s()} will write a space character. + * @param quoteChar The character to write when {@link #q()} is called. + * @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>). + */ + public SerializerWriter(Writer out, boolean useIndentation, boolean useWhitespace, char quoteChar, String relativeUriBase, String absolutePathUriBase) { + this.out = out; + this.useIndentation = useIndentation; + this.useWhitespace = useWhitespace; + this.quoteChar = quoteChar; + this.relativeUriBase = relativeUriBase; + this.absolutePathUriBase = absolutePathUriBase; + } + + /** + * Performs a carriage return. + * <p> + * Adds a newline and the specified number of tabs (if the {@code useIndentation} setting is enabled) to the output. + * + * @param depth The indentation. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter cr(int depth) throws IOException { + if (useIndentation) + return nl().i(depth); + return this; + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text, + * followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param indent The number of tabs to indent. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter appendln(int indent, String text) throws IOException { + return append(indent, true, text); + } + + /** + * Writes the specified text followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter appendln(String text) throws IOException { + return append(0, true, text); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text. + * + * @param indent The number of tabs to indent. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter append(int indent, String text) throws IOException { + return append(indent, false, text); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text. + * + * @param indent The number of tabs to indent. + * @param c The character to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object. + */ + public SerializerWriter append(int indent, char c) throws IOException { + return i(indent).append(c); + } + + /** + * Writes an indent (if the {@code useIndentation} setting is enabled), followed by text, + * optionally followed by a newline (if the {@code useIndentation} setting is enabled). + * + * @param indent The number of tabs to indent. + * @param newline If <jk>true</jk>, then a newline is written. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + private SerializerWriter append(int indent, boolean newline, String text) throws IOException { + i(indent); + out.write(text); + if (newline) + nl(); + return this; + } + + /** + * Appends the specified object as a URI. + * <p> + * Object is converted to a <code>String</code> using <code>toString()</code>, so this will work on {@link URL} or {@link URI} objects, + * or any other type that returns a URI via it's <code>toString()</code> method. + * <p> + * If the URI is relative (i.e. without a schema and not prepended with <js>'/'</js>) the URI + * will be prepended with {@link #absolutePathUriBase} and {@link #relativeUriBase}. + * <p> + * If the URI is context-absolute (i.e. without a schema, but prepended with <js>'/'</js>) + * the URI will be prepended with {@link #absolutePathUriBase}. + * + * @param uri The URI to serialize. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter appendUri(Object uri) throws IOException { + String s = uri.toString(); + if (s.indexOf("://") == -1) { + if (StringUtils.startsWith(s, '/')) { + if (absolutePathUriBase != null) + append(absolutePathUriBase); + } else { + if (relativeUriBase != null) { + append(relativeUriBase); + if (! relativeUriBase.equals("/")) + append("/"); + + } + } + } + return append(s); + } + + /** + * Adds a whitespace character to the output if the {@code useWhitespace} setting is enabled. + * + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter s() throws IOException { + if (useWhitespace) + out.write(' '); + return this; + } + + /** + * Adds the quote character specified by the {@code quoteChar} setting to the output. + * + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + */ + public SerializerWriter q() throws IOException { + out.write(quoteChar); + return this; + } + + /** + * Writes an indent to the writer if the {@code useIndentation} setting is enabled. + * + * @param indent The number of tabs to indent. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter i(int indent) throws IOException { + if (useIndentation) + for (int i = 0; i < indent; i++) + out.write('\t'); + return this; + } + + /** + * Writes a newline to the writer if the {@code useIndentation} setting is enabled. + * + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter nl() throws IOException { + if (useIndentation) + out.write('\n'); + return this; + } + + /** + * Writes the specified text to the writer. + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter append(Object text) throws IOException { + out.append(text == null ? null : text.toString()); + return this; + } + + /** + * Writes the specified text to the writer. + * + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter append(String text) throws IOException { + if (text != null) + out.append(text); + return this; + } + + /** + * Writes the specified text to the writer if b is true. + * + * @param b Boolean flag. + * @param text The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter appendIf(boolean b, String text) throws IOException { + if (b) + out.write(text); + return this; + } + + /** + * Writes the specified text to the writer if b is true. + * + * @param b Boolean flag. + * @param c The text to write. + * @throws IOException If a problem occurred trying to write to the writer. + * @return This object (for method chaining). + */ + public SerializerWriter appendIf(boolean b, char c) throws IOException { + if (b) + out.write(c); + return this; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Writer */ + public SerializerWriter append(char c) throws IOException { + out.write(c); + return this; + } + + @Override /* Writer */ + public void write(char[] cbuf, int off, int len) throws IOException { + out.write(cbuf, off, len); + } + + @Override /* Writer */ + public void flush() throws IOException { + out.flush(); + } + + @Override /* Writer */ + public void close() throws IOException { + out.close(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.class new file mode 100755 index 0000000..a6fcb1d Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.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/serializer/StringObject.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java new file mode 100755 index 0000000..ae9a506 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/StringObject.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * 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.serializer; + +import java.io.*; + +import com.ibm.juno.core.*; + +/** + * A serializer/object pair used for delayed object serialization. + * <p> + * Useful in certain conditions such as logging when you don't want to needlessly serialize objects. + * <p> + * Instances of this method are created by the {@link WriterSerializer#toStringObject(Object)} method. + * + * <h6 class='topic'>Example</h6> + * <p class='bcode'> + * <jc>// The POJO will not be serialized unless DEBUG is enabled.</jc> + * logger.log(<jsf>DEBUG</jsf>, <js>"Object contents are: {0}"</js>, JsonSerializer.<jsf>DEFAULT</jsf>.toObjectString(myPojo)); + * </p> + * + * @author James Bognar ([email protected]) + */ +public class StringObject implements CharSequence, Writable { + + private final WriterSerializer s; + private final Object o; + private String results; + + /** + * Constructor. + * @param s The serializer to use to serialize the object. + * @param o The object to be serialized. + */ + public StringObject(WriterSerializer s, Object o) { + this.s = s; + this.o = o; + } + + @Override /* Object */ + public String toString() { + if (results == null) + results = s.toString(o); + return results; + } + + @Override /* CharSequence */ + public int length() { + return toString().length(); + } + + @Override /* CharSequence */ + public char charAt(int index) { + return toString().charAt(index); + } + + @Override /* CharSequence */ + public CharSequence subSequence(int start, int end) { + return toString().subSequence(start, end); + } + + @Override /* Writable */ + public void writeTo(Writer w) throws IOException { + try { + s.serialize(o, w); + } catch (SerializeException e) { + throw new IOException(e); + } + } + + @Override /* Writable */ + public String getMediaType() { + return s.getMediaTypes()[0]; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.class new file mode 100755 index 0000000..a1b81b4 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.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/serializer/WriterSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java new file mode 100755 index 0000000..6309176 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/WriterSerializer.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * 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.serializer; + +import java.io.*; +import java.lang.reflect.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; + +/** + * Subclass of {@link Serializer} for character-based serializers. + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This class is typically the parent class of all character-based serializers. + * It has 2 abstract methods to implement... + * <ul> + * <li>{@link #createContext(ObjectMap, Method)} + * <li>{@link #doSerialize(Object, Writer, SerializerContext)} + * </ul> + * + * + * <h6 class='topic'>@Produces annotation</h6> + * <p> + * The media types that this serializer can produce is specified through the {@link Produces @Produces} annotation. + * <p> + * However, the media types can also be specified programmatically by overriding the {@link #getMediaTypes()} + * and {@link #getResponseContentType()} methods. + * + * @author James Bognar ([email protected]) + */ +public abstract class WriterSerializer extends Serializer<Writer> { + + @Override /* Serializer */ + public boolean isWriterSerializer() { + return true; + } + + //-------------------------------------------------------------------------------- + // Abstract methods + //-------------------------------------------------------------------------------- + + @Override /* Serializer */ + protected abstract void doSerialize(Object o, Writer out, SerializerContext ctx) throws IOException, SerializeException; + + + //-------------------------------------------------------------------------------- + // Other methods + //-------------------------------------------------------------------------------- + + /** + * Internal test method. + * + * @param o The object to serialize. + * @param ctx The serialize context. + * This object is automatically closed after serialization. + * @return The output serialized to a string. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public final String serialize(Object o, SerializerContext ctx) throws SerializeException { + try { + StringWriter w = new StringWriter(); + serialize(o, w, ctx); + return w.toString(); + } catch (IOException e) { // Shouldn't happen. + throw new RuntimeException(e); + } + } + + /** + * Convenience method for serializing an object to a String. + * + * @param o The object to serialize. + * @return The output serialized to a string. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public final String serialize(Object o) throws SerializeException { + try { + StringWriter w = new StringWriter(); + serialize(o, w, createContext()); + return w.toString(); + } catch (IOException e) { + throw new RuntimeException(e); // Shouldn't happen. + } + } + + /** + * Identical to {@link #serialize(Object)} except throws a {@link RuntimeException} + * instead of a {@link SerializeException}. + * This is typically good enough for debugging purposes. + * + * @param o The object to serialize. + * @return The serialized object. + */ + public final String toString(Object o) { + try { + StringWriter w = new StringWriter(); + serialize(o, w, createContext()); + return w.toString(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * Wraps the specified object inside a {@link StringObject}. + * + * @param o The object to wrap. + * @return The wrapped object. + */ + public final StringObject toStringObject(Object o) { + return new StringObject(this, o); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* CoreApi */ + public WriterSerializer addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public WriterSerializer addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> WriterSerializer addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public WriterSerializer setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public WriterSerializer lock() { + super.lock(); + return this; + } + + @Override /* CoreApi */ + public WriterSerializer clone() throws CloneNotSupportedException { + WriterSerializer c = (WriterSerializer)super.clone(); + return c; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html new file mode 100755 index 0000000..4fe443f --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/serializer/package.html @@ -0,0 +1,128 @@ +<!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>Serializer API</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> + +<a id='TOC'></a><h5 class='toc'>Table of Contents</h5> +<ol class='toc'> + <li><p><a class='doclink' href='#Serializer'>Serializer API</a></p> + <ol> + <li><p><a class='doclink' href='#SerializerGroup'>The SerializerGroup class</a></p> + </ol> + <li><p><a class='doclink' href='#DefiningSerializer'>Defining a new Serializer</a></p> +</ol> + +<!-- ======================================================================================================== --> +<a id="Serializer"></a> +<h2 class='topic' onclick='toggle(this)'>1 - Serializer API</h2> +<div class='topic'> + <p> + The serialization API is designed to be easily extensible by developers.<br> + If you are writing your own serializer, you will typically subclass directly from either {@link com.ibm.juno.core.serializer.WriterSerializer} + or {@link com.ibm.juno.core.serializer.OutputStreamSerializer}.<br> + </p> + + <!-- ======================================================================================================== --> + <a id="SerializerGroup"></a> + <h3 class='topic' onclick='toggle(this)'>1.1 - The SerializerGroup class</h3> + <div class='topic'> + <p> + The {@link com.ibm.juno.core.serializer.SerializerGroup} class represents a group of serializers registered with the media types they handle. + </p> + + <h6 class='topic'>Features</h6> + <p> + The <code>SerializerGroup</code> class provides the following features: + <ul> + <li>Finds serializers based on HTTP <code>Accept</code> header values. + <li>Sets common properties on all serializers in a single method call. + <li>Locks all serializers in a single method call. + <li>Clones existing groups and all serializers within the group in a single method call. + </ul> + + <p> + Refer to {@link com.ibm.juno.core.serializer.SerializerGroup} for additional information. + </p> + </div> +</div> + + +<!-- ======================================================================================================== --> +<a id="DefiningSerializer"></a> +<h2 class='topic' onclick='toggle(this)'>2 - Defining a new Serializer</h2> +<div class='topic'> + <p> + Defining a new serializer is quite simple if you subclass directly from {@link com.ibm.juno.core.serializer.WriterSerializer} + or {@link com.ibm.juno.core.serializer.OutputStreamSerializer}.<br> + In each case, you simply need to implement a single + method and specify a {@link com.ibm.juno.core.annotation.Produces} annotation. + </p> + <p> + The following example shows a simple serializer that converts images to output streams using standard JRE classes. + </p> + <p class='bcode'> + <ja>@Produces</ja>({<js>"image/png"</js>,<js>"image/jpeg"</js>}) + <jk>public static class</jk> ImageSerializer <jk>extends</jk> OutputStreamSerializer { + <ja>@Override</ja> + <jk>public void</jk> serialize(Object o, OutputStream out, SerializerContext ctx) <jk>throws</jk> IOException, SerializeException { + RenderedImage image = (RenderedImage)o; + String mediaType = ctx.getMediaType(); + ImageIO.<jsm>write</jsm>(image, mediaType.substring(mediaType.indexOf(<js>'/'</js>)+1), out); + } + } + </p> + <p> + Serializer that take advantage of the entire {@link com.ibm.juno.core.CoreApi} interface to be able to serialize arbitrary beans and POJOs is + considerably more complex and outside the scope of this document.<br> + If developing such a serializer, the best course of action would be to replicate what occurs in the {@link com.ibm.juno.core.json.JsonSerializer} class. + </p> +</div> + +</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/soap/SoapXmlSerializer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.class new file mode 100755 index 0000000..1d9487b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.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/soap/SoapXmlSerializer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java new file mode 100755 index 0000000..df249f2 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializer.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * 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.soap; + +import static com.ibm.juno.core.soap.SoapXmlSerializerProperties.*; + +import java.io.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.annotation.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.xml.*; + +/** + * Serializes POJOs to HTTP responses as XML+SOAP. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Accept</code> types: <code>text/xml+soap</code> + * <p> + * Produces <code>Content-Type</code> types: <code>text/xml+soap</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * Essentially the same output as {@link XmlDocSerializer}, except wrapped in a standard SOAP envelope. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link SoapXmlSerializerProperties} + * <li>{@link XmlSerializerProperties} + * <li>{@link SerializerProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@Produces(value="text/xml+soap",contentType="text/xml") +public final class SoapXmlSerializer extends XmlSerializer { + + //-------------------------------------------------------------------------------- + // 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("?>"); + w.oTag("soap", "Envelope") + .attr("xmlns", "soap", xctx.getProperties().getString(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope")) + .appendln(">"); + w.sTag(1, "soap", "Body").nl(); + super.serialize(o, w, ctx); + w.eTag(1, "soap", "Body").nl(); + w.eTag("soap", "Envelope").nl(); + } + + @Override /* Serializer */ + public ObjectMap getResponseHeaders(ObjectMap properties) { + return super.getResponseHeaders(properties) + .append("SOAPAction", properties.getString(SOAPXML_SOAPAction, "http://www.w3.org/2003/05/soap-envelope")); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.class new file mode 100755 index 0000000..9aac21c Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.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/soap/SoapXmlSerializerProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java new file mode 100755 index 0000000..f66a7f2 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/SoapXmlSerializerProperties.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * 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.soap; + +/** + * Properties associated with the {@link SoapXmlSerializer} class. + * + * @author James Bognar ([email protected]) + */ +public final class SoapXmlSerializerProperties { + + /** + * The <code>SOAPAction</code> HTTP header value to set on responses. + * <p> + * Default is <js>"http://www.w3.org/2003/05/soap-envelope"</js>. + */ + public static final String SOAPXML_SOAPAction = "SoapXmlSerializer.SOAPAction"; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html new file mode 100755 index 0000000..3b4f818 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/soap/package.html @@ -0,0 +1,34 @@ +<!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>SOAP/XML serialization and parsing support</p> +</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/urlencoding/UonParser$Decoding.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.class new file mode 100755 index 0000000..c4824b2 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser$Decoding.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/urlencoding/UonParser.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.class new file mode 100755 index 0000000..1a58860 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.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/urlencoding/UonParser.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java new file mode 100755 index 0000000..6638562 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParser.java @@ -0,0 +1,861 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2013, 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.urlencoding; + +import static com.ibm.juno.core.urlencoding.UonParserProperties.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +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.utils.*; + +/** + * Parses UON (a notation for URL-encoded query parameter values) text into POJO models. + * + * + * <h6 class='topic'>Media types</h6> + * <p> + * Handles <code>Content-Type</code> types: <code>text/uon</code> + * + * + * <h6 class='topic'>Description</h6> + * <p> + * This parser uses a state machine, which makes it very fast and efficient. + * + * + * <h6 class='topic'>Configurable properties</h6> + * <p> + * This class has the following properties associated with it: + * <ul> + * <li>{@link UonParserProperties} + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * + * @author James Bognar ([email protected]) + */ +@SuppressWarnings({ "rawtypes", "unchecked" }) +@Consumes("text/uon") +public class UonParser extends ReaderParser { + + /** Reusable instance of {@link UonParser}, all default settings. */ + public static final UonParser DEFAULT = new UonParser().lock(); + + /** Reusable instance of {@link UonParser.Decoding}. */ + public static final UonParser DEFAULT_DECODING = new Decoding().lock(); + + /** Reusable instance of {@link UonParser}, all default settings, whitespace-aware. */ + public static final UonParser DEFAULT_WS_AWARE = new UonParser().setProperty(UON_whitespaceAware, true).lock(); + + // Characters that need to be preceeded with an escape character. + private static final AsciiSet escapedChars = new AsciiSet(",()~=$\u0001\u0002"); + + private static final char NUL='\u0000', AMP='\u0001', EQ='\u0002'; // Flags set in reader to denote & and = characters. + + /** + * Equivalent to <code><jk>new</jk> UrlEncodingParser().setProperty(UonParserProperties.<jsf>UON_decodeChars</jsf>,<jk>true</jk>);</code>. + */ + public static class Decoding extends UonParser { + /** Constructor */ + public Decoding() { + setProperty(UON_decodeChars, true); + } + } + + /** UON parser properties currently set on this serializer. */ + protected transient UonParserProperties upp = new UonParserProperties(); + + /** URL-Encoding properties currently set on this serializer. */ + protected transient UrlEncodingProperties uep = new UrlEncodingProperties(); + + /** + * Workhorse method. + * + * @param nt The class type being parsed, or <jk>null</jk> if unknown. + * @param ctx The parser context for this parse. + * @param r The reader being parsed. + * @param p If this is a bean property value, the bean property meta, or null if this is not a bean property value being parsed. + * @param outer The outer object (for constructing nested inner classes). + * @param isUrlParamValue If <jk>true</jk>, then we're parsing a top-level URL-encoded value which is treated a bit different than the default case. + * @param name The parent field or map key name. + * @return The parsed object. + * @throws ParseException + */ + protected <T> T parseAnything(ClassMeta<T> nt, UonParserContext ctx, ParserReader r, BeanPropertyMeta p, Object outer, boolean isUrlParamValue, Object name) throws ParseException { + + BeanContext bc = ctx.getBeanContext(); + if (nt == null) + nt = (ClassMeta<T>)object(); + PojoFilter<T,Object> filter = (PojoFilter<T,Object>)nt.getPojoFilter(); + ClassMeta<?> ft = nt.getFilteredClassMeta(); + + int line = r.getLine(); + int column = r.getColumn(); + Object o = null; + try { + // Parse type flag '$x' + char flag = readFlag(r, (char)0); + + int c = r.peek(); + + if (c == -1 || c == AMP) { + // If parameter is blank and it's an array or collection, return an empty list. + if (ft.isArray() || ft.isCollection()) + o = ft.newInstance(); + else if (ft.isString() || ft.isObject()) + o = ""; + else if (ft.isPrimitive()) + o = ft.getPrimitiveDefault(); + // Otherwise, leave null. + } else if (ft.isObject()) { + if (flag == 0 || flag == 's') { + o = parseString(r, isUrlParamValue, ctx); + } else if (flag == 'b') { + o = parseBoolean(r, ctx); + } else if (flag == 'n') { + o = parseNumber(r, null, ctx); + } else if (flag == 'o') { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + o = m.cast(); + } else if (flag == 'a') { + Collection l = new ObjectList(bc); + o = parseIntoCollection(ctx, r, l, ft.getElementType(), isUrlParamValue); + } else { + throw new ParseException(line, column, "Unexpected flag character ''{0}''.", flag); + } + } else if (ft.isBoolean()) { + o = parseBoolean(r, ctx); + } else if (ft.isCharSequence()) { + o = parseString(r, isUrlParamValue, ctx); + } else if (ft.isChar()) { + String s = parseString(r, isUrlParamValue, ctx); + o = s == null ? null : s.charAt(0); + } else if (ft.isNumber()) { + o = parseNumber(r, (Class<? extends Number>)ft.getInnerClass(), ctx); + } 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()); + } else if (ft.isCollection()) { + if (flag == 'o') { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + // Handle case where it's a collection, but serialized as a map with a _class or _value key. + if (m.containsKey("_class") || m.containsKey("_value")) + o = m.cast(); + // Handle case where it's a collection, but only a single value was specified. + else { + Collection l = (ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance(outer) : new ObjectList(bc)); + l.add(m.cast(ft.getElementType())); + o = l; + } + } else { + Collection l = (ft.canCreateNewInstance(outer) ? (Collection)ft.newInstance(outer) : new ObjectList(bc)); + o = parseIntoCollection(ctx, r, l, ft.getElementType(), isUrlParamValue); + } + } 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)) { + BeanMap m = bc.newBeanMap(outer, ft.getInnerClass()); + m = parseIntoBeanMap(ctx, r, m); + o = m == null ? null : m.getBean(); + } else if (ft.canCreateNewInstanceFromString(outer)) { + String s = parseString(r, isUrlParamValue, ctx); + if (s != null) + o = ft.newInstanceFromString(outer, s); + } else if (ft.isArray()) { + if (flag == 'o') { + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + // Handle case where it's an array, but serialized as a map with a _class or _value key. + if (m.containsKey("_class") || m.containsKey("_value")) + o = m.cast(); + // Handle case where it's an array, but only a single value was specified. + else { + ArrayList l = new ArrayList(1); + l.add(m.cast(ft.getElementType())); + o = bc.toArray(ft, l); + } + } else { + ArrayList l = (ArrayList)parseIntoCollection(ctx, r, new ArrayList(), ft.getElementType(), isUrlParamValue); + o = bc.toArray(ft, l); + } + } else if (flag == 'o') { + // It could be a non-bean with _class attribute. + ObjectMap m = new ObjectMap(bc); + parseIntoMap(ctx, r, m, string(), object()); + if (m.containsKey("_class")) + o = m.cast(); + else + throw new ParseException(line, column, "Class ''{0}'' could not be instantiated. Reason: ''{1}''", ft.getInnerClass().getName(), ft.getNotABeanReason()); + } else { + throw new ParseException(line, column, "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 (name != null) + setName(nt, o, name); + + return (T)o; + + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { + if (p == null) + throw new ParseException("Error occurred trying to parse into class ''{0}''", ft).initCause(e); + throw new ParseException("Error occurred trying to parse value for bean property ''{0}'' on class ''{1}''", + p.getName(), p.getBeanMeta().getClassMeta() + ).initCause(e); + } + } + + private <K,V> Map<K,V> parseIntoMap(UonParserContext ctx, ParserReader r, Map<K,V> m, ClassMeta<K> keyType, ClassMeta<V> valueType) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + if (keyType == null) + keyType = (ClassMeta<K>)string(); + + int c = r.read(); + if (c == -1 || c == NUL || c == AMP) + return null; + if (c != '(') + throw new ParseException(line, column, "Expected '(' at beginning of object."); + + final int S1=1; // Looking for attrName start. + final int S2=2; // Found attrName end, looking for =. + final int S3=3; // Found =, looking for valStart. + final int S4=4; // Looking for , or ) + boolean isInEscape = false; + + int state = S1; + K currAttr = null; + while (c != -1 && c != AMP) { + c = r.read(); + if (! isInEscape) { + if (state == S1) { + if (c == ')') + return m; + if ((c == '\n' || c == '\r') && ctx.whitespaceAware) + skipSpace(r); + else { + r.unread(); + Object attr = parseAttr(r, ctx.decodeChars, ctx); + currAttr = (attr == null ? null : ctx.getBeanContext().convertToType(attr, keyType)); + state = S2; + c = 0; // Avoid isInEscape if c was '\' + } + } else if (state == S2) { + if (c == EQ || c == '=') + state = S3; + else if (c == -1 || c == ',' || c == ')' || c == AMP) { + if (currAttr == null) { + // Value was '%00' + r.unread(); + return null; + } + m.put(currAttr, null); + if (c == ')' || c == -1 || c == AMP) + return m; + state = S1; + } + } else if (state == S3) { + if (c == -1 || c == ',' || c == ')' || c == AMP) { + V value = convertAttrToType(m, "", valueType); + m.put(currAttr, value); + if (c == -1 || c == ')' || c == AMP) + return m; + state = S1; + } else { + V value = parseAnything(valueType, ctx, r.unread(), null, m, false, currAttr); + m.put(currAttr, value); + state = S4; + c = 0; // Avoid isInEscape if c was '\' + } + } else if (state == S4) { + if (c == ',') + state = S1; + else if (c == ')' || c == -1 || c == AMP) { + return m; + } + } + } + isInEscape = isInEscape(c, r, isInEscape); + } + if (state == S1) + throw new ParseException(line, column, "Could not find attribute name on object."); + if (state == S2) + throw new ParseException(line, column, "Could not find '=' following attribute name on object."); + if (state == S3) + throw new ParseException(line, column, "Dangling '=' found in object entry"); + if (state == S4) + throw new ParseException(line, column, "Could not find ')' marking end of object."); + + return null; // Unreachable. + } + + private <E> Collection<E> parseIntoCollection(UonParserContext ctx, ParserReader r, Collection<E> l, ClassMeta<E> elementType, boolean isUrlParamValue) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int c = r.read(); + if (c == -1 || c == NUL || c == AMP) + return null; + + // If we're parsing a top-level parameter, we're allowed to have comma-delimited lists outside parenthesis (e.g. "&foo=1,2,3&bar=a,b,c") + // This is not allowed at lower levels since we use comma's as end delimiters. + boolean isInParens = (c == '('); + if (! isInParens) + if (isUrlParamValue) + r.unread(); + else + throw new ParseException(line, column, "Could not find '(' marking beginning of collection."); + + if (isInParens) { + final int S1=1; // Looking for starting of first entry. + final int S2=2; // Looking for starting of subsequent entries. + final int S3=3; // Looking for , or ) after first entry. + + int state = S1; + while (c != -1 && c != AMP) { + c = r.read(); + if (state == S1 || state == S2) { + if (c == ')') { + if (state == S2) { + l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null)); + r.read(); + } + return l; + } else if ((c == '\n' || c == '\r') && ctx.whitespaceAware) { + skipSpace(r); + } else { + l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null)); + state = S3; + } + } else if (state == S3) { + if (c == ',') { + state = S2; + } else if (c == ')') { + return l; + } + } + } + if (state == S1 || state == S2) + throw new ParseException(line, column, "Could not find start of entry in array."); + if (state == S3) + throw new ParseException(line, column, "Could not find end of entry in array."); + + } else { + final int S1=1; // Looking for starting of entry. + final int S2=2; // Looking for , or & or END after first entry. + + int state = S1; + while (c != -1 && c != AMP) { + c = r.read(); + if (state == S1) { + if ((c == '\n' || c == '\r') && ctx.whitespaceAware) { + skipSpace(r); + } else { + l.add(parseAnything(elementType, ctx, r.unread(), null, l, false, null)); + state = S2; + } + } else if (state == S2) { + if (c == ',') { + state = S1; + } else if ((c == '\n' || c == '\r') && ctx.whitespaceAware) { + skipSpace(r); + } else if (c == AMP || c == -1) { + r.unread(); + return l; + } + } + } + } + + return null; // Unreachable. + } + + private <T> BeanMap<T> parseIntoBeanMap(UonParserContext ctx, ParserReader r, BeanMap<T> m) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + int c = r.read(); + if (c == -1 || c == NUL || c == AMP) + return null; + if (c != '(') + throw new ParseException(line, column, "Expected '(' at beginning of object."); + + final int S1=1; // Looking for attrName start. + final int S2=2; // Found attrName end, looking for =. + final int S3=3; // Found =, looking for valStart. + final int S4=4; // Looking for , or } + boolean isInEscape = false; + + int state = S1; + String currAttr = ""; + int currAttrLine = -1, currAttrCol = -1; + while (c != -1 && c != AMP) { + c = r.read(); + if (! isInEscape) { + if (state == S1) { + if (c == ')' || c == -1 || c == AMP) { + return m; + } + if ((c == '\n' || c == '\r') && ctx.whitespaceAware) + skipSpace(r); + else { + r.unread(); + currAttrLine= r.getLine(); + currAttrCol = r.getColumn(); + currAttr = parseAttrName(r, ctx.decodeChars); + if (currAttr == null) // Value was '%00' + return null; + state = S2; + } + } else if (state == S2) { + if (c == EQ || c == '=') + state = S3; + else if (c == -1 || c == ',' || c == ')' || c == AMP) { + m.put(currAttr, null); + if (c == ')' || c == -1 || c == AMP) + return m; + state = S1; + } + } else if (state == S3) { + if (c == -1 || c == ',' || c == ')' || c == AMP) { + if (! currAttr.equals("_class")) { + BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + m.put(currAttr, ""); + } else { + onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol); + } + } else { + Object value = ctx.getBeanContext().convertToType("", pMeta.getClassMeta()); + pMeta.set(m, value); + } + } + if (c == -1 || c == ')' || c == AMP) + return m; + state = S1; + } else { + if (! currAttr.equals("_class")) { + BeanPropertyMeta pMeta = m.getPropertyMeta(currAttr); + if (pMeta == null) { + if (m.getMeta().isSubTyped()) { + m.put(currAttr, parseAnything(object(), ctx, r.unread(), null, m.getBean(false), false, currAttr)); + } else { + onUnknownProperty(ctx, currAttr, m, currAttrLine, currAttrCol); + parseAnything(object(), ctx, r.unread(), null, m.getBean(false), false, null); // Read content anyway to ignore it + } + } else { + Object value = parseAnything(pMeta.getClassMeta(), ctx, r.unread(), pMeta, m.getBean(false), false, currAttr); + pMeta.set(m, value); + } + } + state = S4; + } + } else if (state == S4) { + if (c == ',') + state = S1; + else if (c == ')' || c == -1 || c == AMP) { + return m; + } + } + } + isInEscape = isInEscape(c, r, isInEscape); + } + if (state == S1) + throw new ParseException(line, column, "Could not find attribute name on object."); + if (state == S2) + throw new ParseException(line, column, "Could not find '=' following attribute name on object."); + if (state == S3) + throw new ParseException(line, column, "Could not find value following '=' on object."); + if (state == S4) + throw new ParseException(line, column, "Could not find ')' marking end of object."); + + return null; // Unreachable. + } + + Object parseAttr(ParserReader r, boolean encoded, UonParserContext ctx) throws IOException, ParseException { + Object attr; + int c = r.peek(); + if (c == '$') { + char f = readFlag(r, (char)0); + if (f == 'b') + attr = parseBoolean(r, ctx); + else if (f == 'n') + attr = parseNumber(r, null, ctx); + else + attr = parseAttrName(r, encoded); + } else { + attr = parseAttrName(r, encoded); + } + return attr; + } + + String parseAttrName(ParserReader r, boolean encoded) throws IOException, ParseException { + + // If string is of form '(xxx)', we're looking for ')' at the end. + // Otherwise, we're looking for '&' or '=' or -1 denoting the end of this string. + + int line = r.getLine(); + int column = r.getColumn(); + int c = r.peek(); + if (c == '$') + readFlag(r, 's'); + if (c == '(') + return parsePString(r); + + r.mark(); + boolean isInEscape = false; + if (encoded) { + while (c != -1) { + c = r.read(); + if (! isInEscape) { + if (c == AMP || c == EQ || c == -1) { + if (c != -1) + r.unread(); + String s = r.getMarked(); + return (s.equals("\u0000") ? null : s); + } + } + else if (c == AMP) + r.replace('&'); + else if (c == EQ) + r.replace('='); + isInEscape = isInEscape(c, r, isInEscape); + } + } else { + while (c != -1) { + c = r.read(); + if (! isInEscape) { + if (c == '=' || c == -1) { + if (c != -1) + r.unread(); + String s = r.getMarked(); + return (s.equals("\u0000") ? null : s); + } + } + isInEscape = isInEscape(c, r, isInEscape); + } + } + + // We should never get here. + throw new ParseException(line, column, "Unexpected condition."); + } + + + /** + * Returns true if the next character in the stream is preceeded by an escape '~' character. + * @param c The current character. + * @param r The reader. + * @param prevIsInEscape What the flag was last time. + */ + private static final boolean isInEscape(int c, ParserReader r, boolean prevIsInEscape) throws IOException { + if (c == '~' && ! prevIsInEscape) { + c = r.peek(); + if (escapedChars.contains(c)) { + r.delete(); + return true; + } + } + return false; + } + + String parseString(ParserReader r, boolean isUrlParamValue, UonParserContext ctx) throws IOException, ParseException { + + // If string is of form '(xxx)', we're looking for ')' at the end. + // Otherwise, we're looking for ',' or ')' or -1 denoting the end of this string. + + int c = r.peek(); + if (c == '(') + return parsePString(r); + + r.mark(); + boolean isInEscape = false; + String s = null; + AsciiSet endChars = (isUrlParamValue ? endCharsParam : endCharsNormal); + while (c != -1) { + c = r.read(); + if (! isInEscape) { + // If this is a URL parameter value, we're looking for: & + // If not, we're looking for: &,) + if (endChars.contains(c)) { + r.unread(); + c = -1; + } + } + if (c == -1) + s = r.getMarked(); + else if (c == EQ) + r.replace('='); + else if ((c == '\n' || c == '\r') && ctx.whitespaceAware) { + s = r.getMarked(0, -1); + skipSpace(r); + c = -1; + } + isInEscape = isInEscape(c, r, isInEscape); + } + + return (s == null || s.equals("\u0000") ? null : s); + } + + private static final AsciiSet endCharsParam = new AsciiSet(""+AMP), endCharsNormal = new AsciiSet(",)"+AMP); + + + /** + * Parses a string of the form "(foo)" + * All whitespace within parenthesis are preserved. + */ + static String parsePString(ParserReader r) throws IOException, ParseException { + + int line = r.getLine(); + int column = r.getColumn(); + r.read(); // Skip first parenthesis. + r.mark(); + int c = 0; + + boolean isInEscape = false; + while (c != -1) { + c = r.read(); + if (! isInEscape) { + if (c == ')') + return r.getMarked(0, -1); + } + if (c == EQ) + r.replace('='); + isInEscape = isInEscape(c, r, isInEscape); + } + throw new ParseException(line, column, "Unmatched parenthesis"); + } + + private Boolean parseBoolean(ParserReader r, UonParserContext ctx) throws IOException, ParseException { + int line = r.getLine(); + int column = r.getColumn(); + readFlag(r, 'b'); + String s = parseString(r, false, ctx); + if (s == null) + return null; + if (s.equals("true")) + return true; + if (s.equals("false")) + return false; + throw new ParseException(line, column, "Unrecognized syntax for boolean. ''{0}''.", s); + } + + private Number parseNumber(ParserReader r, Class<? extends Number> c, UonParserContext ctx) throws IOException, ParseException { + readFlag(r, 'n'); + String s = parseString(r, false, ctx); + if (s == null) + return null; + return StringUtils.parseNumber(s, c); + } + + /* + * Call this method after you've finished a parsing a string to make sure that if there's any + * remainder in the input, that it consists only of whitespace and comments. + */ + private void validateEnd(ParserReader r) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + int c = r.read(); + if (c != -1) + throw new ParseException(line, column, "Remainder after parse: ''{0}''.", (char)c); + } + + /** + * Reads flag character from "$x(" construct if flag is present. + * Returns 0 if no flag is present. + */ + static char readFlag(ParserReader r, char expected) throws IOException, ParseException { + int line = r.getLine(); + int column = r.getColumn(); + char c = (char)r.peek(); + if (c == '$') { + r.read(); + char f = (char)r.read(); + if (expected != 0 && f != expected) + throw new ParseException(line, column, "Unexpected flag character: ''{0}''. Expected ''{1}''.", f, expected); + c = (char)r.peek(); + // Type flag must be followed by '(' + if (c != '(') + throw new ParseException(line, column, "Unexpected character following flag: ''{0}''.", c); + return f; + } + return 0; + } + + private Object[] parseArgs(UonParserContext ctx, ParserReader r, ClassMeta<?>[] argTypes) throws ParseException, IOException { + int line = r.getLine(); + int column = r.getColumn(); + + final int S1=1; // Looking for start of entry + final int S2=2; // Looking for , or ) + + Object[] o = new Object[argTypes.length]; + int i = 0; + + int c = r.read(); + if (c == -1 || c == AMP) + return null; + if (c != '(') + throw new ParseException("Expected '(' at beginning of args array."); + + int state = S1; + while (c != -1 && c != AMP) { + c = r.read(); + if (state == S1) { + if (c == ')') + return o; + o[i] = parseAnything(argTypes[i], ctx, r.unread(), null, ctx.getOuter(), false, null); + i++; + state = S2; + } else if (state == S2) { + if (c == ',') { + state = S1; + } else if (c == ')') { + return o; + } + } + } + + throw new ParseException(line, column, "Did not find ')' at the end of args array."); + } + + private static void skipSpace(ParserReader r) throws IOException { + int c = 0; + while ((c = r.read()) != -1) { + if (c > ' ' || c <= 2) { + r.unread(); + return; + } + } + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Parser */ + public UonParserContext createContext(ObjectMap properties, Method javaMethod, Object outer) { + return new UonParserContext(getBeanContext(), pp, upp, uep, properties, javaMethod, outer); + } + + @Override /* Parser */ + protected <T> T doParse(Reader in, int estimatedSize, ClassMeta<T> type, ParserContext ctx) throws ParseException, IOException { + UonParserContext uctx = (UonParserContext)ctx; + type = ctx.getBeanContext().normalizeClassMeta(type); + UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize); + T o = parseAnything(type, uctx, r, null, ctx.getOuter(), true, null); + validateEnd(r); + return o; + } + + @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 { + UonParserContext uctx = (UonParserContext)ctx; + UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize); + readFlag(r, 'o'); + m = parseIntoMap(uctx, r, m, ctx.getBeanContext().getClassMeta(keyType), ctx.getBeanContext().getClassMeta(valueType)); + validateEnd(r); + return m; + } + + @Override /* ReaderParser */ + protected <E> Collection<E> doParseIntoCollection(Reader in, int estimatedSize, Collection<E> c, Type elementType, ParserContext ctx) throws ParseException, IOException { + UonParserContext uctx = (UonParserContext)ctx; + UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize); + readFlag(r, 'a'); + c = parseIntoCollection(uctx, r, c, ctx.getBeanContext().getClassMeta(elementType), false); + validateEnd(r); + return c; + } + + @Override /* ReaderParser */ + protected Object[] doParseArgs(Reader in, int estimatedSize, ClassMeta<?>[] argTypes, ParserContext ctx) throws ParseException, IOException { + UonParserContext uctx = (UonParserContext)ctx; + UonParserReader r = uctx.getUrlEncodingParserReader(in, estimatedSize); + readFlag(r, 'a'); + Object[] a = parseArgs(uctx, r, argTypes); + return a; + } + + @Override /* Parser */ + public UonParser setProperty(String property, Object value) throws LockedException { + checkLock(); + if (! upp.setProperty(property, value)) + if (! uep.setProperty(property, value)) + super.setProperty(property, value); + return this; + } + + @Override /* CoreApi */ + public UonParser setProperties(ObjectMap properties) throws LockedException { + for (Map.Entry<String,Object> e : properties.entrySet()) + setProperty(e.getKey(), e.getValue()); + return this; + } + + @Override /* CoreApi */ + public UonParser addNotBeanClasses(Class<?>...classes) throws LockedException { + super.addNotBeanClasses(classes); + return this; + } + + @Override /* CoreApi */ + public UonParser addFilters(Class<?>...classes) throws LockedException { + super.addFilters(classes); + return this; + } + + @Override /* CoreApi */ + public <T> UonParser addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + super.addImplClass(interfaceClass, implClass); + return this; + } + + @Override /* CoreApi */ + public UonParser setClassLoader(ClassLoader classLoader) throws LockedException { + super.setClassLoader(classLoader); + return this; + } + + @Override /* Lockable */ + public UonParser lock() { + super.lock(); + return this; + } + + @Override /* Lockable */ + public UonParser clone() { + try { + UonParser c = (UonParser)super.clone(); + c.upp = upp.clone(); + c.uep = uep.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/urlencoding/UonParserContext.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.class new file mode 100755 index 0000000..87fa9f6 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.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/urlencoding/UonParserContext.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java new file mode 100755 index 0000000..631bdcd --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserContext.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * 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.urlencoding; + +import static com.ibm.juno.core.urlencoding.UonParserProperties.*; +import static com.ibm.juno.core.urlencoding.UrlEncodingProperties.*; + +import java.io.*; +import java.lang.reflect.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; + +/** + * Context object that lives for the duration of a single parsing in {@link UonParser} and {@link UrlEncodingParser}. + * <p> + * + * @author James Bognar ([email protected]) + */ +public class UonParserContext extends ParserContext { + + boolean decodeChars, whitespaceAware, expandedParams; + + /** + * Create a new parser context with the specified options. + * + * @param beanContext The bean context being used. + * @param pp The default parser properties. + * @param upp The default UON-Encoding properties. + * @param uep The default URL-Encoding properties. + * @param op The override properties. + * @param javaMethod The java method that called this parser, usually the method in a REST servlet. + * @param outer The outer object for instantiating top-level non-static inner classes. + */ + public UonParserContext(BeanContext beanContext, ParserProperties pp, UonParserProperties upp, UrlEncodingProperties uep, ObjectMap op, Method javaMethod, Object outer) { + super(beanContext, pp, op, javaMethod, outer); + if (op == null || op.isEmpty()) { + decodeChars = upp.decodeChars; + whitespaceAware = upp.whitespaceAware; + expandedParams = uep.expandedParams; + } else { + decodeChars = op.getBoolean(UON_decodeChars, upp.decodeChars); + whitespaceAware = op.getBoolean(UON_whitespaceAware, upp.whitespaceAware); + expandedParams = op.getBoolean(URLENC_expandedParams, uep.expandedParams); + } + } + + /** + * Returns the {@link UrlEncodingProperties#URLENC_expandedParams} setting value in this context. + * + * @return The {@link UrlEncodingProperties#URLENC_expandedParams} setting value in this context. + */ + public final boolean isExpandedParams() { + return expandedParams; + } + + /** + * Wraps the specified reader in a {@link UonParserReader}. + * + * @param r The reader to wrap. + * @param estimatedSize The estimated size of the input. + * @return The wrapped reader. + */ + public final UonParserReader getUrlEncodingParserReader(Reader r, int estimatedSize) { + if (r instanceof UonParserReader) + return (UonParserReader)r; + return new UonParserReader(r, Math.min(8096, estimatedSize), decodeChars); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.class new file mode 100755 index 0000000..cffaddd Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.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/urlencoding/UonParserProperties.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java new file mode 100755 index 0000000..f7b16ef --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserProperties.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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.urlencoding; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.parser.*; + +/** + * Configurable properties on the {@link UonParser} and {@link UrlEncodingParser} classes. + * <p> + * Use the {@link UonParser#setProperty(String, Object)} method to set property values. + * <p> + * In addition to these properties, the following properties are also applicable for {@link UonParser}. + * <ul> + * <li>{@link ParserProperties} + * <li>{@link BeanContextProperties} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public class UonParserProperties implements Cloneable { + + /** + * Decode <js>"%xx"</js> sequences. ({@link Boolean}, default=<jk>false</jk> for {@link UonParser}, <jk>true</jk> for {@link UrlEncodingParser}). + * <p> + * Specify <jk>true</jk> if URI encoded characters should be decoded, <jk>false</jk> + * if they've already been decoded before being passed to this parser. + */ + public static final String UON_decodeChars = "UonParser.decodeChars"; + + /** + * Expect input to contain readable whitespace characters from using the {@link UonSerializerProperties#UON_useWhitespace} setting ({@link Boolean}, default=<jk>false</jk>). + */ + public static final String UON_whitespaceAware = "UonParser.whitespaceAware"; + + boolean + decodeChars = false, + whitespaceAware = false; + + /** + * Sets the specified property value. + * @param property The property name. + * @param value The property value. + * @return <jk>true</jk> if property name was valid and property was set. + */ + public boolean setProperty(String property, Object value) { + BeanContext bc = BeanContext.DEFAULT; + if (property.equals(UON_decodeChars)) + decodeChars = bc.convertToType(value, Boolean.class); + else if (property.equals(UON_whitespaceAware)) + whitespaceAware = bc.convertToType(value, Boolean.class); + else + return false; + return true; + } + + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Cloneable */ + public UonParserProperties clone() { + try { + return (UonParserProperties)super.clone(); + } 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/urlencoding/UonParserReader.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class new file mode 100755 index 0000000..f8585fb Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/urlencoding/UonParserReader.class differ
