http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.java new file mode 100755 index 0000000..304bc25 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/CoreApi.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * 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; + +/** + * Common super class for all core-API serializers, parsers, and serializer/parser groups. + * + * <h6 class='topic'>Description</h6> + * <p> + * Maintains an inner {@link BeanContextFactory} instance that can be used by serializer and parser subclasses + * to work with beans in a consistent way. + * <p> + * Provides several duplicate convenience methods from the {@link BeanContextFactory} class to set properties on that class from this class. + * <p> + * Also implements the {@link Lockable} interface to allow for easy locking and cloning. + * + * + * @author James Bognar ([email protected]) + */ +public abstract class CoreApi extends Lockable { + + /** The bean context used by this object. */ + protected transient BeanContextFactory beanContextFactory = new BeanContextFactory(); + private BeanContext beanContext; + + + /** + * Returns the current value of the {@code beanContext} setting. + * + * @return The current setting value. + */ + public final BeanContext getBeanContext() { + if (beanContext == null) + beanContext = beanContextFactory.getBeanContext(); + return beanContext; + } + + /** + * Sets a property on this class. + * + * @param property The property name. + * @param value The property value. + * @return This class (for method chaining). + * @throws LockedException If {@link #lock()} has been called on this object. + */ + public CoreApi setProperty(String property, Object value) throws LockedException { + checkLock(); + beanContextFactory.setProperty(property, value); + return this; + } + + /** + * Sets multiple properties on this class. + * + * @param properties The properties to set on this class. + * @return This class (for method chaining). + * @throws LockedException If {@link #lock()} has been called on this object. + */ + public CoreApi setProperties(ObjectMap properties) throws LockedException { + checkLock(); + beanContextFactory.setProperties(properties); + return this; + } + + /** + * Shortcut for calling <code>getBeanContext().addNotBeanClasses(Class...)</code>. + * + * @see BeanContextFactory#addNotBeanClasses(Class...) + * @param classes The new setting value for the bean context. + * @throws LockedException If {@link BeanContextFactory#lock()} was called on this class or the bean context. + * @return This object (for method chaining). + */ + public CoreApi addNotBeanClasses(Class<?>...classes) throws LockedException { + checkLock(); + beanContextFactory.addNotBeanClasses(classes); + return this; + } + + /** + * Shortcut for calling <code>getBeanContext().addFilters(Class...)</code>. + * + * @see BeanContextFactory#addFilters(Class...) + * @param classes The new setting value for the bean context. + * @throws LockedException If {@link BeanContextFactory#lock()} was called on this class or the bean context. + * @return This object (for method chaining). + */ + public CoreApi addFilters(Class<?>...classes) throws LockedException { + checkLock(); + beanContextFactory.addFilters(classes); + return this; + } + + /** + * Shortcut for calling <code>getBeanContext().addImplClass(Class, Class)</code>. + * + * @see BeanContextFactory#addImplClass(Class, Class) + * @param interfaceClass The interface class. + * @param implClass The implementation class. + * @throws LockedException If {@link BeanContextFactory#lock()} was called on this class or the bean context. + * @param <T> The class type of the interface. + * @return This object (for method chaining). + */ + public <T> CoreApi addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { + checkLock(); + beanContextFactory.addImplClass(interfaceClass, implClass); + return this; + } + + /** + * Shortcut for calling <code>getBeanContext().setClassLoader(ClassLoader)</code>. + * + * @see BeanContextFactory#setClassLoader(ClassLoader) + * @param classLoader The new classloader. + * @throws LockedException If {@link BeanContextFactory#lock()} was called on this class or the bean context. + * @return This object (for method chaining). + */ + public CoreApi setClassLoader(ClassLoader classLoader) throws LockedException { + checkLock(); + beanContextFactory.setClassLoader(classLoader); + return this; + } + + /** + * Shortcut for calling {@link BeanContext#object()}. + * + * @return The reusable {@link ClassMeta} for representing the {@link Object} class. + */ + public ClassMeta<Object> object() { + return getBeanContext().object(); + } + + /** + * Shortcut for calling {@link BeanContext#string()}. + * + * @return The reusable {@link ClassMeta} for representing the {@link String} class. + */ + public ClassMeta<String> string() { + return getBeanContext().string(); + } + + //-------------------------------------------------------------------------------- + // Overridden methods + //-------------------------------------------------------------------------------- + + @Override /* Lockable */ + public void checkLock() { + super.checkLock(); + beanContext = null; + } + + @Override /* Lockable */ + public CoreApi lock() { + super.lock(); + beanContextFactory.lock(); + beanContext = beanContextFactory.getBeanContext(); + return this; + } + + @Override /* Lockable */ + public CoreApi clone() throws CloneNotSupportedException{ + CoreApi c = (CoreApi)super.clone(); + c.beanContextFactory = beanContextFactory.clone(); + c.beanContext = null; + 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/Delegate.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.class new file mode 100755 index 0000000..1099224 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.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/Delegate.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.java new file mode 100755 index 0000000..27e1b6d --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Delegate.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * 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; + +/** + * An object that represents another object, often wrapping that object. + * <p> + * <b>*** Internal Interface - Not intended for external use ***</b> + * <p> + * For example, {@link BeanMap} is a map representation of a bean. + * + * @author James Bognar ([email protected]) + * @param <T> The represented class type. + */ +public interface Delegate<T> { + + /** + * The {@link ClassMeta} of the class of the represented object. + * + * @return The class type of the represented object. + */ + public ClassMeta<T> getClassMeta(); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.class new file mode 100755 index 0000000..9ad36e2 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.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/InvalidDataConversionException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.java new file mode 100755 index 0000000..4353979 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/InvalidDataConversionException.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2011, 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; + +import java.text.*; + +/** + * General invalid conversion exception. + * <p> + * Exception that gets thrown if you try to perform an invalid conversion, such as when calling {@code ObjectMap.getInt(...)} on a non-numeric <code>String</code>. + * + * @author James Bognar ([email protected]) + */ +public final class InvalidDataConversionException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * @param toType Attempting to convert to this class type. + * @param cause The cause. + * @param value The value being converted. + */ + public InvalidDataConversionException(Object value, Class<?> toType, Exception cause) { + super(MessageFormat.format("Invalid data conversion from type ''{0}'' to type ''{1}''. Value=''{2}''.", value == null ? null : value.getClass().getName(), toType.getName(), value), cause); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.class new file mode 100755 index 0000000..0600ad8 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.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/Lockable.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.java new file mode 100755 index 0000000..0b8ecd8 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Lockable.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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; + +/** + * Superclass of all classes that have a locked state. + * <p> + * Used to mark bean contexts, serializers, and parsers as read-only so that + * settings can no longer be modified. + * <p> + * Also keeps track of when the object has been cloned and allows for lazy cloning through + * the {@link #onUnclone()} method. The idea behind this is that certain expensive fields don't + * need to be cloned unless the object is actually being modified. + * <p> + * Calling {@link #lock()} on the object causes it to be put into a read-only state. + * Once called, subsequent calls to {@link #checkLock()} will cause {@link LockedException LockedExceptions} + * to be thrown. + * <p> + * As a rule, cloned objects are unlocked by default. + * + * @author James Bognar ([email protected]) + */ +public abstract class Lockable implements Cloneable { + + private boolean isLocked = false; + private boolean isCloned = false; + + /** + * Locks this object so that settings on it cannot be modified. + * + * @return This object (for method chaining). + */ + public Lockable lock() { + isLocked = true; + return this; + } + + /** + * @return <code><jk>true</jk></code> if this object has been locked. + */ + public boolean isLocked() { + return isLocked; + } + + /** + * Causes a {@link LockedException} to be thrown if this object has been locked. + * <p> + * Also calls {@link #onUnclone()} if this is the first time this method has been called since cloning. + * + * @throws LockedException If {@link #lock()} has been called on this object. + */ + public void checkLock() throws LockedException { + if (isLocked) + throw new LockedException(); + if (isCloned) + onUnclone(); + isCloned = false; + } + + /** + * Subclass can override this method to handle lazy-cloning on the first time {@link #checkLock()} is called after + * the object has been cloned. + */ + public void onUnclone() {} + + /** + * Creates an unlocked clone of this object. + * + * @throws CloneNotSupportedException If class cannot be cloned. + */ + @Override /* Object */ + public Lockable clone() throws CloneNotSupportedException { + Lockable c = (Lockable)super.clone(); + c.isLocked = false; + c.isCloned = true; + 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/LockedException.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.class new file mode 100755 index 0000000..422d647 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.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/LockedException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.java new file mode 100755 index 0000000..ce2f31e --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/LockedException.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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; + +/** + * Exception that gets thrown when trying to modify settings on a locked {@link Lockable} object. + * <p> + * A locked exception indicates a programming error. + * Certain objects that are meant for reuse, such as serializers and parsers, provide + * the ability to lock the current settings so that they cannot be later changed. + * This exception indicates that a setting change was attempted on a previously locked object. + * + * @author James Bognar ([email protected]) + */ +public final class LockedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + LockedException() { + super("Object is locked and object settings cannot be modified."); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.class new file mode 100755 index 0000000..53d21c1 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.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/MediaRange.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.java new file mode 100755 index 0000000..c10e07c --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/MediaRange.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * 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; + +import java.util.*; +import java.util.Map.Entry; + +/** + * Describes a single type used in content negotiation between an HTTP client and server, as described in + * Section 14.1 and 14.7 of RFC2616 (the HTTP/1.1 specification). + */ +public final class MediaRange implements Comparable<MediaRange> { + + private final String type; // The media type (e.g. "text" for Accept, "utf-8" for Accept-Charset) + private final String subType; // The media sub-type (e.g. "json" for Accept, not used for Accept-Charset) + private final Float qValue; + private final Map<String,Set<String>> parameters, extensions; + + /** + * Returns the media type enclosed by this media range. + * <p> + * Examples: + * <ul> + * <li><js>"text/html"</js> + * <li><js>"text/*"</js> + * <li><js>"*\/*"</js> + * </ul> + * + * @return The media type of this media range, lowercased, never <jk>null</jk>. + */ + public String getMediaType() { + return type + "/" + subType; + } + + /** + * Return just the type portion of this media range. + * + * @return The type portion of this media range. + */ + public String getType() { + return type; + } + + /** + * Returns the <js>'q'</js> (quality) value for this type, as described in Section 3.9 of RFC2616. + * <p> + * The quality value is a float between <code>0.0</code> (unacceptable) and <code>1.0</code> (most acceptable). + * <p> + * If 'q' value doesn't make sense for the context (e.g. this range was extracted from a <js>"content-*"</js> header, as opposed to <js>"accept-*"</js> + * header, its value will always be <js>"1"</js>. + * + * @return The 'q' value for this type, never <jk>null</jk>. + */ + public Float getQValue() { + return qValue; + } + + /** + * Returns the optional set of parameters associated to the type as returned by {@link #getMediaType()}. + * <p> + * The parameters are those values as described in standardized MIME syntax. + * An example of such a parameter in string form might be <js>"level=1"</js>. + * <p> + * Values are lowercase and never <jk>null</jk>. + * + * @return The optional list of parameters, never <jk>null</jk>. + */ + public Map<String,Set<String>> getParameters() { + return parameters; + } + + /** + * Returns the optional set of custom extensions defined for this type. + * <p> + * Values are lowercase and never <jk>null</jk>. + * + * @return The optional list of extensions, never <jk>null</jk>. + */ + public Map<String,Set<String>> getExtensions() { + return extensions; + } + + /** + * Provides a string representation of this media range, suitable for use as an <code>Accept</code> header value. + * <p> + * The literal text generated will be all lowercase. + * + * @return A media range suitable for use as an Accept header value, never <code>null</code>. + */ + @Override /* Object */ + public String toString() { + StringBuffer sb = new StringBuffer().append(type).append('/').append(subType); + + if (! parameters.isEmpty()) + for (Entry<String,Set<String>> e : parameters.entrySet()) { + String k = e.getKey(); + for (String v : e.getValue()) + sb.append(';').append(k).append('=').append(v); + } + + // '1' is equivalent to specifying no qValue. If there's no extensions, then we won't include a qValue. + if (qValue.floatValue() == 1.0) { + if (! extensions.isEmpty()) { + sb.append(";q=").append(qValue); + for (Entry<String,Set<String>> e : extensions.entrySet()) { + String k = e.getKey(); + for (String v : e.getValue()) + sb.append(';').append(k).append('=').append(v); + } + } + } else { + sb.append(";q=").append(qValue); + for (Entry<String,Set<String>> e : extensions.entrySet()) { + String k = e.getKey(); + for (String v : e.getValue()) + sb.append(';').append(k).append('=').append(v); + } + } + return sb.toString(); + } + + /** + * Returns <jk>true</jk> if the specified object is also a <code>MediaType</code>, and has the same qValue, type, parameters, and extensions. + * + * @return <jk>true</jk> if object is equivalent. + */ + @Override /* Object */ + public boolean equals(Object o) { + + if (o == null || !(o instanceof MediaRange)) + return false; + + if (this == o) + return true; + + MediaRange o2 = (MediaRange) o; + return qValue.equals(o2.qValue) + && type.equals(o2.type) + && subType.equals(o2.subType) + && parameters.equals(o2.parameters) + && extensions.equals(o2.extensions); + } + + /** + * Returns a hash based on this instance's <code>media-type</code>. + * + * @return A hash based on this instance's <code>media-type</code>. + */ + @Override /* Object */ + public int hashCode() { + return type.hashCode() + subType.hashCode(); + } + + /** + * Creates a <code>MediaRange</code> object with the referenced values. + * + * @param type The MIME type of this media range (e.g. <js>"application"</js> in <js>"application/json"</js>) + * @param subType The MIME subtype of this media range (e.g. <js>"json"</js> in <js>"application/json"</js>). + * @param parameters The optional parameters for this range. + * @param qValue The quality value of this range. Must be between <code>0</code> and <code>1.0</code>. + * @param extensions The optional extensions to this quality value. + */ + private MediaRange(String type, String subType, Map<String,Set<String>> parameters, Float qValue, Map<String,Set<String>> extensions) { + this.type = type; + this.subType = subType; + this.parameters = (parameters == null ? new TreeMap<String,Set<String>>() : parameters); + this.extensions = (extensions == null ? new TreeMap<String,Set<String>>() : extensions); + this.qValue = qValue; + } + + /** + * Parses an <code>Accept</code> header value into an array of media ranges. + * <p> + * The returned media ranges are sorted such that the most acceptable media is available at ordinal position <js>'0'</js>, and the least acceptable at position n-1. + * <p> + * The syntax expected to be found in the referenced <code>value</code> complies with the syntax described in RFC2616, Section 14.1, as described below: + * <p class='bcode'> + * Accept = "Accept" ":" + * #( media-range [ accept-params ] ) + * + * media-range = ( "*\/*" + * | ( type "/" "*" ) + * | ( type "/" subtype ) + * ) *( ";" parameter ) + * accept-params = ";" "q" "=" qvalue *( accept-extension ) + * accept-extension = ";" token [ "=" ( token | quoted-string ) ] + * </p> + * This method can also be used on other headers such as <code>Accept-Charset</code> and <code>Accept-Encoding</code>... + * <p class='bcode'> + * Accept-Charset = "Accept-Charset" ":" + * 1#( ( charset | "*" )[ ";" "q" "=" qvalue ] ) + * </p> + * + * @param value The value to parse. If <jk>null</jk> or empty, returns a single <code>MediaRange</code> is returned that represents all types. + * @return The media ranges described by the string. + * The ranges are sorted such that the most acceptable media is available at ordinal position <js>'0'</js>, and the least acceptable at position n-1. + */ + public static MediaRange[] parse(String value) { + + Set<MediaRange> ranges = new TreeSet<MediaRange>(); + + if (value == null || value.length() == 0) + return new MediaRange[]{new MediaRange("*", "*", null, 1f, null)}; + + value = value.toLowerCase(Locale.ENGLISH); + + for (String r : value.trim().split("\\s*,\\s*")) { + r = r.trim(); + + if (r.isEmpty()) + continue; + + String[] tokens = r.split("\\s*;\\s*"); + + tokens[0] = tokens[0].replace(' ', '+'); + + // There is at least a type. + String[] t = tokens[0].split("/"); + String type = t[0], subType = (t.length == 1 ? "*" : t[1]); + + // Only the type of the range is specified + if (tokens.length == 1) { + ranges.add(new MediaRange(type, subType, null, 1f, null)); + continue; + } + + Float qValue = 1f; + Map<String,Set<String>> params = new TreeMap<String,Set<String>>(); + Map<String,Set<String>> exts = new TreeMap<String,Set<String>>(); + + boolean isInExtensions = false; + for (int i = 1; i < tokens.length; i++) { + String[] parm = tokens[i].split("\\s*=\\s*"); + if (parm.length == 2) { + String k = parm[0], v = parm[1]; + if (isInExtensions) { + if (! exts.containsKey(parm[0])) + exts.put(parm[0], new TreeSet<String>()); + exts.get(parm[0]).add(parm[1]); + } else if (k.equals("q")) { + qValue = new Float(v); + isInExtensions = true; + } else /*(! isInExtensions)*/ { + if (! params.containsKey(parm[0])) + params.put(parm[0], new TreeSet<String>()); + params.get(parm[0]).add(parm[1]); + } + } + } + + ranges.add(new MediaRange(type, subType, params, qValue, exts)); + } + + return ranges.toArray(new MediaRange[ranges.size()]); + } + + /** + * Compares two MediaRanges for equality. + * <p> + * The values are first compared according to <code>qValue</code> values. + * Should those values be equal, the <code>type</code> is then lexicographically compared (case-insensitive) in ascending order, + * with the <js>"*"</js> type demoted last in that order. + * <code>MediaRanges</code> with the same type but different sub-types are compared - a more specific subtype is + * promoted over the 'wildcard' subtype. + * <code>MediaRanges</code> with the same types but with extensions are promoted over those same types with no extensions. + * + * @param o The range to compare to. Never <jk>null</jk>. + */ + @Override /* Comparable */ + public int compareTo(MediaRange o) { + + // Compare q-values. + int qCompare = Float.compare(o.qValue, qValue); + if (qCompare != 0) + return qCompare; + + // Compare media-types. + // Note that '*' comes alphabetically before letters, so just do a reverse-alphabetical comparison. + int i = o.type.compareTo(type); + if (i == 0) + i = o.subType.compareTo(subType); + return i; + } + + /** + * Returns <jk>true</jk> if the specified <code>MediaRange</code> matches this range. + * <p> + * This implies the types and subtypes are the same as or encompasses the other (e.g. <js>'application/xml'</js> and <js>'application/*'</js>). + * + * @param o The other media rage. + * @return <jk>true</jk> if the media ranges are the same or one encompasses the other. + */ + public boolean matches(MediaRange o) { + if (this == o) + return true; + + if (qValue == 0 || o.qValue == 0) + return false; + + if (type.equals(o.type) || (type.equals("*")) || (o.type.equals("*"))) + if (subType.equals(o.subType) || subType.equals("*") || o.subType.equals("*")) + return true; + + return false; + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$1.class new file mode 100755 index 0000000..cb9fa76 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$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/ObjectList$2$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2$1.class new file mode 100755 index 0000000..229273b Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2$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/ObjectList$2.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2.class new file mode 100755 index 0000000..5dba15e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList$2.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/ObjectList.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.class new file mode 100755 index 0000000..3204758 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.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/ObjectList.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.java new file mode 100755 index 0000000..74f00b0 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectList.java @@ -0,0 +1,447 @@ +/******************************************************************************* + * 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; + +import java.io.*; +import java.util.*; + +import com.ibm.juno.core.json.*; +import com.ibm.juno.core.parser.*; +import com.ibm.juno.core.serializer.*; +import com.ibm.juno.core.utils.*; + +/** + * Java implementation of a JSON array. + * <p> + * An extension of {@link LinkedList}, so all methods available to in that class are also available + * to this class. + * <p> + * Note that the use of this class is optional. The serializers will accept any objects that implement + * the {@link Collection} interface. But this class provides some useful additional functionality + * when working with JSON models constructed from Java Collections Framework objects. For example, a + * constructor is provided for converting a JSON array string directly into a {@link List}. It also contains + * accessor methods for to avoid common typecasting when accessing elements in a list. + * + * <h6 class='topic'>Examples</h6> + * <p class='bcode'> + * <jc>// Construct an empty List</jc> + * List l = <jk>new</jk> ObjectList(); + * + * <jc>// Construct a list of objects using various methods</jc> + * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>).append(123).append(<jk>true</jk>); + * l = <jk>new</jk> ObjectList().append(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> + * l = <jk>new</jk> ObjectList(<js>"foo"</js>, 123, <jk>true</jk>); <jc>// Equivalent</jc> + * + * <jc>// Construct a list of integers from JSON</jc> + * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); + * + * <jc>// Construct a list of generic ObjectMap objects from JSON</jc> + * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); + * + * <jc>// Construct a list of integers from XML</jc> + * String xml = <js>"<array><number>1</number><number>2</number><number>3</number></array>"</js>; + * l = <jk>new</jk> ObjectList(xml, DataFormat.<jsf>XML</jsf>); + * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(xml); <jc>// Equivalent</jc> + * l = (List)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * l = XmlParser.<jsf>DEFAULT</jsf>.parse(List.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * l = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectList.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * + * <jc>// Construct JSON from ObjectList</jc> + * l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:'bing'}]"</js>); + * String json = l.toString(); <jc>// Produces "[{foo:'bar'},{baz:'bing'}]"</jc> + * json = l.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>); <jc>// Equivalent</jc> + * json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(l); <jc>// Equivalent</jc> + * + * <jc>// Get one of the entries in the list as an Integer</jc> + * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); + * Integer i = l.getInt(1); + * i = l.get(Integer.<jk>class</jk>, 1); <jc>// Equivalent</jc> + * + * <jc>// Get one of the entries in the list as an Float</jc> + * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); + * Float f = l.getFloat(1); <jc>// Returns 2f </jc> + * f = l.get(Float.<jk>class</jk>, 1); <jc>// Equivalent</jc> + * + * <jc>// Same as above, except converted to a String</jc> + * l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); + * String s = l.getString(1); <jc>// Returns "2" </jc> + * s = l.get(String.<jk>class</jk>, 1); <jc>// Equivalent</jc> + * + * <jc>// Get one of the entries in the list as a bean (converted to a bean if it isn't already one)</jc> + * l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); + * Person p = l.get(Person.<jk>class</jk>, 0); + * + * <jc>// Iterate over a list of beans using the elements() method</jc> + * ObjectList ObjectList = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); + * <jk>for</jk> (Person p : ObjectList.elements(Person.<jk>class</jk>) { + * <jc>// Do something with p</jc> + * } + * </p> + * + * @author James Bognar ([email protected]) + */ +public class ObjectList extends LinkedList<Object> { + private static final long serialVersionUID = 1L; + + private transient BeanContext beanContext = BeanContext.DEFAULT; + + /** + * An empty read-only ObjectList. + */ + public static final ObjectList EMPTY_LIST = new ObjectList() { + private static final long serialVersionUID = 1L; + + @Override /* List */ + public void add(int location, Object object) { + throw new UnsupportedOperationException(); + } + + @Override /* List */ + public ListIterator<Object> listIterator(final int location) { + return Collections.emptyList().listIterator(location); + } + + @Override /* List */ + public Object remove(int location) { + throw new UnsupportedOperationException(); + } + + @Override /* List */ + public Object set(int location, Object object) { + throw new UnsupportedOperationException(); + } + + @Override /* List */ + public List<Object> subList(int start, int end) { + return Collections.emptyList().subList(start, end); + } + }; + + /** + * Construct a JSON array directly from text using the specified parser. + * + * @param s The string being parsed. + * @param p The parser to use to parse the input. + * @throws ParseException If the input contains a syntax error or is malformed. + */ + public ObjectList(CharSequence s, ReaderParser p) throws ParseException { + this(p == null ? BeanContext.DEFAULT : p.getBeanContext()); + try { + if (p == null) + p = JsonParser.DEFAULT; + if (s != null) + p.parseIntoCollection(new CharSequenceReader(s), s.length(), this, beanContext.object()); + } catch (IOException e) { + throw new ParseException(e); + } + } + + /** + * Shortcut for <code><jk>new</jk> ObjectList(String,JsonParser.<jsf>DEFAULT</jsf>);</code> + * + * @param s The string being parsed. + * @throws ParseException If the input contains a syntax error or is malformed. + */ + public ObjectList(CharSequence s) throws ParseException { + this(s, null); + } + + /** + * Construct a JSON array directly from a reader using the specified parser. + * + * @param r The reader to read from. Will automatically be wrapped in a {@link BufferedReader} if it isn't already a BufferedReader. + * @param p The parser to use to parse the input. + * @throws ParseException If the input contains a syntax error or is malformed. + * @throws IOException If a problem occurred trying to read from the reader. + */ + public ObjectList(Reader r, ReaderParser p) throws ParseException, IOException { + parseReader(r, p); + } + + private void parseReader(Reader r, ReaderParser p) throws IOException, ParseException { + if (p == null) + p = JsonParser.DEFAULT; + p.parseIntoCollection(r, -1, this, beanContext.object()); + } + + /** + * Construct an empty JSON array. (i.e. an empty {@link LinkedList}). + */ + public ObjectList() { + this(BeanContext.DEFAULT); + } + + /** + * Construct an empty JSON array with the specified bean context. (i.e. an empty {@link LinkedList}). + * + * @param beanContext The bean context to associate with this object list for creating beans. + */ + public ObjectList(BeanContext beanContext) { + super(); + this.beanContext = beanContext; + } + + /** + * Construct a JSON array and fill it with the specified objects. + * + * @param o A list of objects to add to this list. + */ + public ObjectList(Object... o) { + super(Arrays.asList(o)); + } + + /** + * Construct a JSON array and fill it with the specified collection of objects. + * + * @param c A list of objects to add to this list. + */ + public ObjectList(Collection<?> c) { + super(c); + } + + /** + * Override the default bean context used for converting POJOs. + * <p> + * Default is {@link BeanContext#DEFAULT}, which is sufficient in most cases. + * <p> + * Useful if you're serializing/parsing beans with filters defined. + * + * @param beanContext The new bean context. + * @return This object (for method chaining). + */ + public ObjectList setBeanContext(BeanContext beanContext) { + this.beanContext = beanContext; + return this; + } + + /** + * Convenience method for adding multiple objects to this list. + * @param o The objects to add to the list. + * @return This object (for method chaining). + */ + public ObjectList append(Object...o) { + for (Object o2 : o) + add(o2); + return this; + } + + /** + * Get the entry at the specified index, converted to the specified type (if possible). + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param type The type of object to convert the entry to. + * @param index The index into this list. + * @param <T> The type of object to convert the entry to. + * @return The converted entry. + */ + public <T> T get(Class<T> type, int index) { + return beanContext.convertToType(get(index), type); + } + + /** + * Shortcut for calling <code>get(String.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + */ + public String getString(int index) { + return get(String.class, index); + } + + /** + * Shortcut for calling <code>get(Integer.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Integer getInt(int index) { + return get(Integer.class, index); + } + + /** + * Shortcut for calling <code>get(Boolean.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Boolean getBoolean(int index) { + return get(Boolean.class, index); + } + + /** + * Shortcut for calling <code>get(Long.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Long getLong(int index) { + return get(Long.class, index); + } + + /** + * Shortcut for calling <code>get(Map.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Map<?,?> getMap(int index) { + return get(Map.class, index); + } + + /** + * Shortcut for calling <code>get(List.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public List<?> getList(int index) { + return get(List.class, index); + } + + /** + * Shortcut for calling <code>get(ObjectMap.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectMap getObjectMap(int index) { + return get(ObjectMap.class, index); + } + + /** + * Shortcut for calling <code>get(ObjectList.<jk>class</jk>, index)</code>. + * + * @param index The index. + * @return The converted value. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectList getObjectList(int index) { + return get(ObjectList.class, index); + } + + /** + * Creates an {@link Iterable} with elements of the specified child type. + * <p> + * Attempts to convert the child objects to the correct type if they aren't already the correct type. + * <p> + * The <code>next()</code> method on the returned iterator may throw a {@link InvalidDataConversionException} if + * the next element cannot be converted to the specified type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for a description of valid conversions. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Iterate over a list of ObjectMaps.</jc> + * ObjectList l = <jk>new</jk> ObjectList(<js>"[{foo:'bar'},{baz:123}]"</js>); + * for (ObjectMap m : l.elements(ObjectMap.<jk>class</jk>)) { + * <jc>// Do something with m.</jc> + * } + * + * <jc>// Iterate over a list of ints.</jc> + * ObjectList l = <jk>new</jk> ObjectList(<js>"[1,2,3]"</js>); + * for (Integer i : l.elements(Integer.<jk>class</jk>)) { + * <jc>// Do something with i.</jc> + * } + * + * <jc>// Iterate over a list of beans.</jc> + * <jc>// Automatically converts to beans.</jc> + * ObjectList l = <jk>new</jk> ObjectList(<js>"[{name:'John Smith',age:45}]"</js>); + * for (Person p : l.elements(Person.<jk>class</jk>)) { + * <jc>// Do something with p.</jc> + * } + * </p> + * </dd> + * </dl> + * + * @param <E> The child object type. + * @param childType The child object type. + * @return A new <code>Iterable</code> object over this list. + */ + public <E> Iterable<E> elements(final Class<E> childType) { + final Iterator<?> i = iterator(); + return new Iterable<E>() { + + @Override /* Iterable */ + public Iterator<E> iterator() { + return new Iterator<E>() { + + @Override /* Iterator */ + public boolean hasNext() { + return i.hasNext(); + } + + @Override /* Iterator */ + public E next() { + return beanContext.convertToType(i.next(), childType); + } + + @Override /* Iterator */ + public void remove() { + i.remove(); + } + + }; + } + }; + } + + /** + * Returns the {@link ClassMeta} of the class of the object at the specified index. + * + * @param index An index into this list, zero-based. + * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null. + */ + public ClassMeta<?> getClassMeta(int index) { + return beanContext.getClassMetaForObject(get(index)); + } + + /** + * Serialize this array to a string using the specified serializer. + * + * @param serializer The serializer to use to convert this object to a string. + * @return This object as a serialized string. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public String toString(WriterSerializer serializer) throws SerializeException { + return serializer.serialize(this); + } + + /** + * Serialize this array to JSON using the {@link JsonSerializer#DEFAULT} serializer. + */ + @Override /* Object */ + public String toString() { + try { + return this.toString(JsonSerializer.DEFAULT_LAX); + } catch (SerializeException e) { + return e.getLocalizedMessage(); + } + } + + /** + * Convenience method for serializing this ObjectList to the specified Writer using + * the JsonSerializer.DEFAULT serializer. + * + * @param w The writer to send the serialized contents of this object. + * @throws IOException If a problem occurred trying to write to the writer. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public void serializeTo(Writer w) throws IOException, SerializeException { + JsonSerializer.DEFAULT.serialize(this); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$1.class new file mode 100755 index 0000000..a58f23d Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$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/ObjectMap$2$1$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$1$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$1$1.class new file mode 100755 index 0000000..57b81a7 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$1$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/ObjectMap$2$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$1.class new file mode 100755 index 0000000..0ccaec1 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2$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/ObjectMap$2.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2.class new file mode 100755 index 0000000..777a312 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap$2.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/ObjectMap.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.class new file mode 100755 index 0000000..5ed2e03 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.class differ
