http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/ContextFactory.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/ContextFactory.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/ContextFactory.java deleted file mode 100644 index becf8f1..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/ContextFactory.java +++ /dev/null @@ -1,1298 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -import static org.apache.juneau.BeanContext.*; - -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.locks.*; - -import org.apache.juneau.internal.*; -import org.apache.juneau.json.*; -import org.apache.juneau.parser.*; - -/** - * A factory for instantiating {@link Context} objects. - * <p> - * The hierarchy of these objects are... - * <ul class='spaced-list'> - * <li>{@link ContextFactory} - A thread-safe, modifiable context property store.<br> - * Used to create {@link Context} objects. - * <li>{@link Context} - A reusable, cachable, thread-safe, read-only context with configuration properties copied from the factory.<br> - * Often used to create {@link Session} objects. - * <li>{@link Session} - A one-time-use non-thread-safe object.<br> - * Used by serializers and parsers to retrieve context properties and to be used as scratchpads. - * </ul> - * - * - * <h6 class='topic'>ContextFactory objects</h6> - * <p> - * Context factories can be thought of as consisting of the following: - * <ul class='spaced-list'> - * <li>A <code>Map<String,Object></code> of context properties. - * <li>A <code>Map<Class,Context></code> of context instances. - * </ul> - * <p> - * Context factories are used to create and cache {@link Context} objects using the {@link #getContext(Class)} method. - * <p> - * As a general rule, {@link ContextFactory} objects are 'slow'.<br> - * Setting and retrieving properties on a factory can involve relatively slow data conversion and synchronization.<br> - * However, the {@link #getContext(Class)} method is fast, and will return cached context objects if the context properties have not changed. - * <p> - * Context factories can be used to store context properties for a variety of contexts.<br> - * For example, a single factory can store context properties for the JSON serializer, XML serializer, HTML serializer - * etc... and can thus be used to retrieve context objects for those serializers.<br> - * <p> - * Other notes: - * <ul class='spaced-list'> - * <li>Context factories can be locked using the {@link #lock()} method.<br> - * This prevents the context properties from being further modified. - * <li>Context factories can be cloned using the {@link #clone} method.<br> - * This will return a new unlocked factory with the same context properties. - * </ul> - * - * <h6 class='topic'>Context properties</h6> - * <p> - * Context properties are 'settings' for serializers and parsers.<br> - * For example, the {@link BeanContext#BEAN_sortProperties} context property defines whether - * bean properties should be serialized in alphabetical order. - * <p> - * Each {@link Context} object should contain the context properties that apply to it as static - * fields (e.g {@link BeanContext#BEAN_sortProperties}). - * <p> - * Context properties can be of the following types: - * <ul class='spaced-list'> - * <li><l>SIMPLE</l> - A simple property.<br> - * Examples include: booleans, integers, Strings, Classes, etc...<br> - * <br> - * An example of this would be the {@link BeanContext#BEAN_sortProperties} property.<br> - * It's name is simply <js>"BeanContext.sortProperties"</js>. - * - * <li><l>SET</l> - A sorted set of objects.<br> - * These are denoted by appending <js>".set"</js> to the property name.<br> - * Objects can be of any type, even complex types.<br> - * Sorted sets use tree sets to maintain the value in alphabetical order.<br> - * <br> - * For example, the {@link BeanContext#BEAN_notBeanClasses} property is used to store classes that should not be treated like beans.<br> - * It's name is <js>"BeanContext.notBeanClasses.set"</js>. - * - * <li><l>LIST</l> - A list of unique objects.<br> - * These are denoted by appending <js>".list"</js> to the property name.<br> - * Objects can be of any type, even complex types.<br> - * Use lists if the ordering of the values in the set is important (similar to how the order of entries in a classpath is important).<br> - * <br> - * For example, the {@link BeanContext#BEAN_transforms} property is used to store transform classes.<br> - * It's name is <js>"BeanContext.transforms.list"</js>. - * - * <li><l>MAP</l> - A sorted map of key-value pairs.<br> - * These are denoted by appending <js>".map"</js> to the property name.<br> - * Keys can be any type directly convertable to and from Strings. - * Values can be of any type, even complex types.<br> - * <br> - * For example, the {@link BeanContext#BEAN_implClasses} property is used to specify the names of implementation classes for interfaces.<br> - * It's name is <js>"BeanContext.implClasses.map"</js>.<br> - * </ul> - * <p> - * All context properties are set using the {@link #setProperty(String, Object)} method. - * <p> - * Default values for context properties can be specified globally as system properties.<br> - * Example: <code>System.<jsm>setProperty</jsm>(<jsf>BEAN_sortProperties</jsf>, <jk>true</jk>);</code> - * <p> - * SET and LIST properties can be added to using the {@link #addToProperty(String, Object)} method and removed from using the {@link #removeFromProperty(String, Object)} method. - * <p> - * SET and LIST properties can also be added to and removed from by appending <js>".add"</js> or <js>".remove"</js> to the property name and using the {@link #setProperty(String, Object)} method. - * <p> - * The following shows the two different ways to append to a set or list property: - * <p class='bcode'> - * Config config = <jk>new</jk> Config().set(<js>"BeanContext.notBeanClasses.set"</js>, Collections.<jsm>emptySet</jsm>()); - * - * <jc>// Append to set property using addTo().</jc> - * config.addTo(<js>"BeanContext.notBeanClasses.set"</js>, MyNotBeanClass.<jk>class</jk>); - * - * <jc>// Append to set property using set().</jc> - * config.set(<js>"BeanContext.notBeanClasses.set.add"</js>, MyNotBeanClass.<jk>class</jk>); - * </p> - * <p> - * Lists are appended to the beginning of the set so that behavior can be overridden.<br> - * <p> - * For sample, the following code shows the order in which POJO transforms are applied.<br> - * In this case, we want F3 and F4 to appear at the beginning of the set so that they - * take precedence over F1 and F2.... - * <p class='bcode'> - * <jc>// Result will be F3,F4,F1,F2</jc> - * config.addTo(<js>"BeanContext.transforms.list"</js>, Arrays.<jsm>asList</jsm>(F1.<jk>class</jk>, F2.<jk>class</jk>)); - * config.addTo(<js>"BeanContext.transforms.list"</js>, Arrays.<jsm>asList</jsm>(F3.<jk>class</jk>,F4.<jk>class</jk>)); - * </p> - * <p> - * SET and LIST properties can also be set and manipulated using JSON strings. - * <p class='bcode'> - * ContextFactory f = ContextFactory.<jsm>create</jsm>(); - * - * <jc>// Set SET value using JSON array. - * f.set(<js>"BeanContext.notBeanClasses.set"</js>, <js>"['com.my.MyNotBeanClass1']"</js>); - * - * <jc>// Add to SET using simple string. - * f.addTo(<js>"BeanContext.notBeanClasses.set"</js>, <js>"com.my.MyNotBeanClass2"</js>); - * - * <jc>// Add an array of values as a JSON array.. - * f.addTo(<js>"BeanContext.notBeanClasses.set"</js>, <js>"['com.my.MyNotBeanClass3']"</js>); - * - * <jc>// Remove an array of values as a JSON array.. - * f.removeFrom(<js>"BeanContext.notBeanClasses.set"</js>, <js>"['com.my.MyNotBeanClass3']"</js>); - * </p> - * <p> - * MAP properties can be added to using the {@link #putToProperty(String, Object, Object)} and {@link #putToProperty(String, Object)} methods.<br> - * MAP property entries can be removed by setting the value to <jk>null</jk> (e.g. <code>config.putTo(<js>"BEAN_implClasses"</js>, MyNotBeanClass.<jk>class</jk>, <jk>null</jk>);</code>.<br> - * MAP properties can also be added to by appending <js>".put"</js> to the property name and using the {@link #setProperty(String, Object)} method.<br> - * <p> - * The following shows the two different ways to append to a set property: - * <p class='bcode'> - * ContextFactory f = ContextFactory.<jsm>create</jsm>().set(<js>"BeanContext.implClasses.map"</js>, Collections.<jsm>emptyMap</jsm>()); - * - * <jc>// Append to map property using putTo().</jc> - * f.putTo(<js>"BeanContext.implClasses.map"</js>, MyInterface.<jk>class</jk>, MyInterfaceImpl.<jk>class</jk>); - * - * <jc>// Append to map property using set().</jc> - * Map m = <jk>new</jk> HashMap(){{put(MyInterface.<jk>class</jk>,MyInterfaceImpl.<jk>class</jk>)}}; - * f.set(<js>"BeanContext.implClasses.map.put"</js>, m); - * </p> - * <p> - * MAP properties can also be set and manipulated using JSON strings. - * <p class='bcode'> - * ContextFactory f = ContextFactory.<jsm>create</jsm>(); - * - * <jc>// Set MAP value using JSON object.</jc> - * f.set(<js>"BeanContext.implClasses.map"</js>, <js>"{'com.my.MyInterface1':'com.my.MyInterfaceImpl1'}"</js>); - * - * <jc>// Add to MAP using JSON object.</jc> - * f.putTo(<js>"BeanContext.implClasses.map"</js>, <js>"{'com.my.MyInterface2':'com.my.MyInterfaceImpl2'}"</js>); - * - * <jc>// Remove from MAP using JSON object.</jc> - * f.putTo(<js>"BeanContext.implClasses.map"</js>, <js>"{'com.my.MyInterface2':null}"</js>); - * </p> - * <p> - * Context properties are retrieved from this factory using the following 3 methods: - * <ul class='spaced-list'> - * <li>{@link #getProperty(String, Class, Object)} - Retrieve a SIMPLE or SET property converted to the specified class type. - * <li>{@link #getMap(String, Class, Class, Map)} - Retrieve a MAP property with keys/values converted to the specified class types. - * <li>{@link #getPropertyMap(String)} - Retrieve a map of all context properties with the specified prefix (e.g. <js>"BeanContext"</js> for {@link BeanContext} properties). - * </ul> - * <p> - * As a general rule, only {@link Context} objects will use these read methods. - * - * - * <h6 class='topic'>Context objects</h6> - * <p> - * A Context object can be thought of as unmodifiable snapshot of a factory.<br> - * They should be 'fast' by avoiding synchronization by using final fields whenever possible.<br> - * However, they MUST be thread safe. - * <p> - * Context objects are created using the {@link #getContext(Class)} method.<br> - * As long as the properties on a factory have not been modified, the factory will return a cached copy - * of a context. - * <p class='bcode'> - * ContextFactory f = ContextFactory.<jsm>create</jsm>(); - * - * <jc>// Get BeanContext with default factory settings.</jc> - * BeanContext bc = f.getContext(BeanContext.<jk>class</jk>); - * - * <jc>// Get another one. This will be the same one.</jc> - * BeanContext bc2 = f.getContext(BeanContext.<jk>class</jk>); - * <jsm>assertTrue</jsm>(bc1 == bc2); - * - * <jc>// Set a property.</jc> - * f.set(<jsf>BEAN_sortProperties</jsf>, <jk>true</jk>); - * - * <jc>// Get another one. This will be different!</jc> - * bc2 = f.getContext(BeanContext.<jk>class</jk>); - * <jsm>assertFalse</jsm>(bc1 == bc2); - * </p> - * - * - * <h6 class='topic'>Session objects</h6> - * <p> - * Session objects are created through {@link Context} objects, typically through a <code>createContext()</code> method.<br> - * Unlike context objects, they are NOT reusable and NOT thread safe.<br> - * They are meant to be used one time and then thrown away.<br> - * They should NEVER need to use synchronization. - * <p> - * Session objects are also often used as scratchpads for information such as keeping track of call stack - * information to detect recursive loops when serializing beans. - * - * - * @author James Bognar ([email protected]) - */ -public final class ContextFactory extends Lockable { - - // All configuration properties in this object. - // Keys are property prefixes (e.g. 'BeanContext'). - // Values are maps containing properties for that specific prefix. - private Map<String,PropertyMap> properties = new ConcurrentHashMap<String,PropertyMap>(); - - // Context cache. - // This gets cleared every time any properties change on this object. - private final Map<Class<? extends Context>,Context> contexts = new ConcurrentHashMap<Class<? extends Context>,Context>(); - - // Global Context cache. - // Context factories that are the 'same' will use the same maps from this cache. - // 'same' means the context properties are all the same when converted to strings. - private static final ConcurrentHashMap<ContextFactory, ConcurrentHashMap<Class<? extends Context>,Context>> globalContextCache = new ConcurrentHashMap<ContextFactory, ConcurrentHashMap<Class<? extends Context>,Context>>(); - - private ReadWriteLock lock = new ReentrantReadWriteLock(); - private Lock rl = lock.readLock(), wl = lock.writeLock(); - - // Classloader used to instantiate Class instances. - ClassLoader classLoader = ClassLoader.getSystemClassLoader(); - - // Parser to use to convert JSON strings to POJOs - ReaderParser defaultParser; - - // Used to keep properties in alphabetical order regardless of whether - // they're not strings. - private static Comparator<Object> PROPERTY_COMPARATOR = new Comparator<Object>() { - @Override - public int compare(Object o1, Object o2) { - return ContextFactory.toString(o1).compareTo(ContextFactory.toString(o2)); - } - }; - - /** - * Create a new context factory with default settings. - * - * @return A new context factory with default settings. - */ - public static ContextFactory create() { - ContextFactory f = new ContextFactory(); - BeanContext.loadDefaults(f); - return f; - } - - /** - * Create a new context factory with settings copied from the specified factory. - * - * @param copyFrom The existing factory to copy properties from. - * @return A new context factory with default settings. - */ - public static ContextFactory create(ContextFactory copyFrom) { - return new ContextFactory().copyFrom(copyFrom); - } - - - ContextFactory() {} - - /** - * Copy constructor. - * - * @param copyFrom The factory to copy properties from. - */ - public ContextFactory(ContextFactory copyFrom) { - copyFrom(copyFrom); - } - - /** - * Copies the properties from the specified factory into this factory. - * - * @param cf The factory to copy from. - * @return This object (for method chaining). - */ - public ContextFactory copyFrom(ContextFactory cf) { - for (Map.Entry<String,PropertyMap> e : cf.properties.entrySet()) - this.properties.put(e.getKey(), new PropertyMap(e.getValue())); - this.classLoader = cf.classLoader; - this.defaultParser = cf.defaultParser; - return this; - } - - /** - * Sets a configuration property value on this object. - * <p> - * A typical usage is to set or overwrite configuration values like so... - * <p class='bcode'> - * ContextFactory g = ContextFactory.<jsm>create</jsm>(); - * f.setProperty(<jsf>BEAN_sortProperties</jsf>, <jk>true</jk>); - * </p> - * <p> - * The possible class types of the value depend on the property type: - * <p> - * <table class='styled'> - * <tr> - * <th>Property type</th> - * <th>Example</th> - * <th>Allowed value type</th> - * </tr> - * <tr> - * <td>Set <l>SIMPLE</l></td> - * <td><js>"Foo.x"</js></td> - * <td>Any object type.</td> - * </tr> - * <tr> - * <td>Set <l>SET/LIST</l></td> - * <td><js>"Foo.x.set"</js></td> - * <td>Any collection or array of any objects, or a String containing a JSON array.</td> - * </tr> - * <tr> - * <td>Add/Remove <l>SET/LIST</l></td> - * <td><js>"Foo.x.set.add"</js></td> - * <td>If a collection, adds or removes the entries in the collection. Otherwise, adds/removes a single entry.</td> - * </tr> - * <tr> - * <td>Set <l>MAP</l></td> - * <td><js>"Foo.x.map"</js></td> - * <td>A map, or a String containing a JSON object. Entries overwrite existing map.</td> - * </tr> - * <tr> - * <td>Put <l>MAP</l></td> - * <td><js>"Foo.x.map.put"</js></td> - * <td>A map, or a String containing a JSON object. Entries are added to existing map.</td> - * </tr> - * </table> - * - * @param name The configuration property name.<br> - * If name ends with <l>.add</l>, then the specified value is added to the - * existing property value as an entry in a SET or LIST property.<br> - * If name ends with <l>.put</l>, then the specified value is added to the - * existing property value as a key/value pair in a MAP property.<br> - * If name ends with <l>.remove</l>, then the specified value is removed from the - * existing property property value in a SET or LIST property.<br> - * - * @param value The new value. - * If <jk>null</jk>, the property value is deleted.<br> - * In general, the value type can be anything.<br> - * - * @return This object (for method chaining). - */ - public ContextFactory setProperty(String name, Object value) { - String prefix = prefix(name); - - if (name.endsWith(".add")) - return addToProperty(name.substring(0, name.lastIndexOf('.')), value); - - if (name.endsWith(".put")) - return putToProperty(name.substring(0, name.lastIndexOf('.')), value); - - if (name.endsWith(".remove")) - return removeFromProperty(name.substring(0, name.lastIndexOf('.')), value); - - wl.lock(); - try { - checkLock(); - contexts.clear(); - if (! properties.containsKey(prefix)) - properties.put(prefix, new PropertyMap(prefix)); - properties.get(prefix).set(name, value); - } finally { - wl.unlock(); - } - return this; - } - - /** - * Convenience method for setting multiple properties in one call. - * <p> - * This appends to any previous configuration properties set on this config. - * - * @param newProperties The new properties to set. - * @return This object (for method chaining). - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public ContextFactory setProperties(Map newProperties) { - wl.lock(); - try { - checkLock(); - contexts.clear(); - for (Map.Entry e : (Set<Map.Entry>)newProperties.entrySet()) { - String name = e.getKey().toString(); - Object value = e.getValue(); - String prefix = prefix(name); - if (name.endsWith(".add")) - addToProperty(name.substring(0, name.lastIndexOf('.')), value); - else if (name.endsWith(".remove")) - removeFromProperty(name.substring(0, name.lastIndexOf('.')), value); - else { - if (! properties.containsKey(prefix)) - properties.put(prefix, new PropertyMap(prefix)); - properties.get(prefix).set(name, value); - } - } - - } finally { - wl.unlock(); - } - return this; - } - - /** - * Adds a value to a SET property. - * - * @param name The property name. - * @param value The new value to add to the SET property. - * @return This object (for method chaining). - * @throws ConfigException If property is not a SET property. - */ - public ContextFactory addToProperty(String name, Object value) { - String prefix = prefix(name); - wl.lock(); - try { - checkLock(); - contexts.clear(); - if (! properties.containsKey(prefix)) - properties.put(prefix, new PropertyMap(prefix)); - properties.get(prefix).addTo(name, value); - } finally { - wl.unlock(); - } - return this; - } - - /** - * Adds or overwrites a value to a MAP property. - * - * @param name The property name. - * @param key The property value map key. - * @param value The property value map value. - * @return This object (for method chaining). - * @throws ConfigException If property is not a MAP property. - */ - public ContextFactory putToProperty(String name, Object key, Object value) { - String prefix = prefix(name); - wl.lock(); - try { - checkLock(); - contexts.clear(); - if (! properties.containsKey(prefix)) - properties.put(prefix, new PropertyMap(prefix)); - properties.get(prefix).putTo(name, key, value); - } finally { - wl.unlock(); - } - return this; - } - - /** - * Adds or overwrites a value to a MAP property. - * - * @param name The property value. - * @param value The property value map value. - * @return This object (for method chaining). - * @throws ConfigException If property is not a MAP property. - */ - public ContextFactory putToProperty(String name, Object value) { - String prefix = prefix(name); - wl.lock(); - try { - checkLock(); - contexts.clear(); - if (! properties.containsKey(prefix)) - properties.put(prefix, new PropertyMap(prefix)); - properties.get(prefix).putTo(name, value); - } finally { - wl.unlock(); - } - return this; - } - - /** - * Removes a value from a SET property. - * - * @param name The property name. - * @param value The property value in the SET property. - * @return This object (for method chaining). - * @throws ConfigException If property is not a SET property. - */ - public ContextFactory removeFromProperty(String name, Object value) { - String prefix = prefix(name); - wl.lock(); - try { - checkLock(); - contexts.clear(); - if (properties.containsKey(prefix)) - properties.get(prefix).removeFrom(name, value); - } finally { - wl.unlock(); - } - return this; - } - - /** - * Returns an instance of the specified context initialized with the properties - * in this config. - * <p> - * Multiple calls to this method for the same config class will return the same - * cached value as long as the config properties on this config are not touched. - * <p> - * As soon as any properties are modified on this config, all cached entries - * are discarded and recreated as needed. - * - * @param c The context class to instantiate. - * @return The context instance. - */ - @SuppressWarnings("unchecked") - public <T extends Context> T getContext(Class<T> c) { - rl.lock(); - try { - try { - if (! contexts.containsKey(c)) { - - // Try to get it from the global cache. - if (! globalContextCache.containsKey(this)) - globalContextCache.putIfAbsent(clone(), new ConcurrentHashMap<Class<? extends Context>,Context>()); - ConcurrentHashMap<Class<? extends Context>, Context> cacheForThisConfig = globalContextCache.get(this); - - if (! cacheForThisConfig.containsKey(c)) - cacheForThisConfig.putIfAbsent(c, c.getConstructor(ContextFactory.class).newInstance(this)); - - contexts.put(c, cacheForThisConfig.get(c)); - } - return (T)contexts.get(c); - } catch (Exception e) { - throw new ConfigException("Could not instantiate config class ''{0}''", className(c)).initCause(e); - } - } finally { - rl.unlock(); - } - } - - /** - * Returns the configuration properties with the specified prefix. - * <p> - * For example, if <l>prefix</l> is <js>"BeanContext"</js>, then retrieves - * all configuration properties that are prefixed with <js>"BeanContext."</js>. - * - * @param prefix The prefix of properties to retrieve. - * @return The configuration properties with the specified prefix, never <jk>null</jk>. - */ - public PropertyMap getPropertyMap(String prefix) { - rl.lock(); - try { - PropertyMap m = properties.get(prefix); - return m == null ? new PropertyMap(prefix) : m; - } finally { - rl.unlock(); - } - } - - /** - * Specifies the classloader to use when resolving classes from strings. - * <p> - * Can be used for resolving class names when the classes being created are in a different - * classloader from the Juneau code. - * <p> - * If <jk>null</jk>, the system classloader will be used to resolve classes. - * - * @param classLoader The new classloader. - * @throws LockedException If {@link #lock()} was called on this object. - * @return This object (for method chaining). - */ - public ContextFactory setClassLoader(ClassLoader classLoader) { - checkLock(); - this.classLoader = (classLoader == null ? ClassLoader.getSystemClassLoader() : classLoader); - return this; - } - - /** - * Specifies the parser to use to convert Strings to POJOs. - * <p> - * If <jk>null</jk>, {@link JsonParser#DEFAULT} will be used. - * - * @param defaultParser The new defaultParser. - * @throws LockedException If {@link #lock()} was called on this object. - * @return This object (for method chaining). - */ - public ContextFactory setDefaultParser(ReaderParser defaultParser) { - checkLock(); - this.defaultParser = defaultParser == null ? JsonParser.DEFAULT : defaultParser; - return this; - } - - /** - * Returns a property value converted to the specified type. - * - * @param name The full name of the property (e.g. <js>"BeanContext.sortProperties"</js>) - * @param type The class type to convert the property value to. - * @param def The default value if the property is not set. - * - * @return The property value. - * @throws ConfigException - If property has a value that cannot be converted to a boolean. - */ - public <T> T getProperty(String name, Class<T> type, T def) { - rl.lock(); - try { - PropertyMap pm = getPropertyMap(prefix(name)); - if (pm != null) - return pm.get(name, type, def); - String s = System.getProperty(name); - if (! StringUtils.isEmpty(s)) - return BeanContext.DEFAULT.convertToType(s, type); - return def; - } finally { - rl.unlock(); - } - } - - /** - * Returns a property value converted to a {@link LinkedHashMap} with the specified - * key and value types. - * - * @param name The full name of the property (e.g. <js>"BeanContext.sortProperties"</js>) - * @param keyType The class type of the keys in the map. - * @param valType The class type of the values in the map. - * @param def The default value if the property is not set. - * - * @return The property value. - * @throws ConfigException - If property has a value that cannot be converted to a boolean. - */ - public <K,V> Map<K,V> getMap(String name, Class<K> keyType, Class<V> valType, Map<K,V> def) { - rl.lock(); - try { - PropertyMap pm = getPropertyMap(prefix(name)); - if (pm != null) - return pm.getMap(name, keyType, valType, def); - return def; - } finally { - rl.unlock(); - } - } - - //------------------------------------------------------------------------------------- - // Convenience methods. - //------------------------------------------------------------------------------------- - - /** - * Shortcut for calling <code>getContext(BeanContext.<jk>class</jk>);</code>. - * - * @return The bean context instance. - */ - public BeanContext getBeanContext() { - return getContext(BeanContext.class); - } - - /** - * Shortcut for calling <code>addTo(<jsf>BEAN_notBeanClasses</jsf>, <jf>classes</jf>)</code>. - * - * @see ContextFactory#addToProperty(String,Object) - * @param classes The new setting value for the bean context. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @return This object (for method chaining). - * @see ContextFactory#addToProperty(String, Object) - * @see BeanContext#BEAN_notBeanClasses - */ - public ContextFactory addNotBeanClasses(Class<?>...classes) throws LockedException { - checkLock(); - addToProperty(BEAN_notBeanClasses, classes); - return this; - } - - /** - * Shortcut for calling <code>addTo(<jsf>BEAN_transforms</jsf>, <jf>classes</jf>)</code>. - * - * @param classes The new setting value for the bean context. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @return This object (for method chaining). - * @see ContextFactory#addToProperty(String, Object) - * @see BeanContext#BEAN_transforms - */ - public ContextFactory addTransforms(Class<?>...classes) throws LockedException { - checkLock(); - addToProperty(BEAN_transforms, classes); - return this; - } - - /** - * Shortcut for calling <code>putTo(<jsf>BEAN_implCLasses</jsf>, <jf>interfaceClass</jf>, <jf>implClass</jf>)</code>. - * - * @param interfaceClass The interface class. - * @param implClass The implementation class. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @param <T> The class type of the interface. - * @return This object (for method chaining). - * @see ContextFactory#putToProperty(String, Object, Object) - * @see BeanContext#BEAN_implClasses - */ - public <T> ContextFactory addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { - checkLock(); - putToProperty(BEAN_implClasses, interfaceClass, implClass); - return this; - } - - - //------------------------------------------------------------------------------------- - // Object methods. - //------------------------------------------------------------------------------------- - - @Override /* Object */ - public int hashCode() { - return this.properties.hashCode(); - } - - @Override /* Object */ - public boolean equals(Object o) { - if (o instanceof ContextFactory) { - ContextFactory c = (ContextFactory)o; - return c.properties.equals(properties); - } - return false; - } - - //-------------------------------------------------------------------------------- - // Utility classes and methods. - //-------------------------------------------------------------------------------- - - /** - * Contains all the properties for a particular property prefix (e.g. <js>'BeanContext'</js>) - * <p> - * Instances of this map are immutable from outside this class. - * <p> - * The {@link PropertyMap#hashCode()} and {@link PropertyMap#equals(Object)} methods - * can be used to compare with other property maps. - * - * @author James Bognar ([email protected]) - */ - @SuppressWarnings("hiding") - public class PropertyMap { - - private Map<String,Property> map = new ConcurrentSkipListMap<String,Property>(); - volatile int hashCode = 0; - ReadWriteLock lock = new ReentrantReadWriteLock(); - Lock rl = lock.readLock(), wl = lock.writeLock(); - - private PropertyMap(String prefix) { - prefix = prefix + '.'; - Properties p = System.getProperties(); - for (Map.Entry<Object,Object> e : p.entrySet()) - if (e.getKey().toString().startsWith(prefix)) - set(e.getKey().toString(), e.getValue()); - } - - /** - * Copy constructor. - */ - private PropertyMap(PropertyMap orig) { - for (Map.Entry<String,Property> e : orig.map.entrySet()) - this.map.put(e.getKey(), Property.create(e.getValue().name, e.getValue().value())); - } - - /** - * Returns the specified property as the specified class type. - * - * @param name The property name. - * @param type The type of object to convert the value to. - * @param def The default value if the specified property is not set. - * - * @return The property value. - */ - public <T> T get(String name, Class<T> type, T def) { - rl.lock(); - try { - Property p = map.get(name); - if (p == null || type == null) - return def; - try { - if (BeanContext.DEFAULT == null) - return def; - return BeanContext.DEFAULT.convertToType(p.value, type); - } catch (InvalidDataConversionException e) { - throw new ConfigException("Could not retrieve config property ''{0}''. {1}", p.name, e.getMessage()); - } - } finally { - rl.unlock(); - } - } - - /** - * Returns the specified property as a map with the specified key and value types. - * <p> - * The map returned is an instance of {@link LinkedHashMap}. - * - * @param name The property name. - * @param keyType The class type of the keys of the map. - * @param valueType The class type of the values of the map. - * @param def The default value if the specified property is not set. - * - * @return The property value. - */ - @SuppressWarnings("unchecked") - public <K,V> Map<K,V> getMap(String name, Class<K> keyType, Class<V> valueType, Map<K,V> def) { - rl.lock(); - try { - Property p = map.get(name); - if (p == null || keyType == null || valueType == null) - return def; - try { - BeanContext bc = BeanContext.DEFAULT; - if (bc != null) - return (Map<K,V>)bc.convertToType(p.value, bc.getMapClassMeta(LinkedHashMap.class, keyType, valueType)); - return def; - } catch (InvalidDataConversionException e) { - throw new ConfigException("Could not retrieve config property ''{0}''. {1}", p.name, e.getMessage()); - } - } finally { - rl.unlock(); - } - } - - /** - * Convenience method for returning all values in this property map as a simple map. - * <p> - * Primarily useful for debugging. - * - * @return A new {@link LinkedHashMap} with all values in this property map. - */ - public Map<String,Object> asMap() { - rl.lock(); - try { - Map<String,Object> m = new LinkedHashMap<String,Object>(); - for (Property p : map.values()) - m.put(p.name, p.value); - return m; - } finally { - rl.unlock(); - } - } - - private void set(String name, Object value) { - wl.lock(); - hashCode = 0; - try { - if (value == null) - map.remove(name); - else - map.put(name, Property.create(name, value)); - } finally { - wl.unlock(); - } - } - - private void addTo(String name, Object value) { - wl.lock(); - hashCode = 0; - try { - if (! map.containsKey(name)) - map.put(name, Property.create(name, Collections.emptyList())); - map.get(name).add(value); - } finally { - wl.unlock(); - } - } - - private void putTo(String name, Object key, Object value) { - wl.lock(); - hashCode = 0; - try { - if (! map.containsKey(name)) - map.put(name, Property.create(name, Collections.emptyMap())); - map.get(name).put(key, value); - } finally { - wl.unlock(); - } - } - - private void putTo(String name, Object value) { - wl.lock(); - hashCode = 0; - try { - if (! map.containsKey(name)) - map.put(name, Property.create(name, Collections.emptyMap())); - map.get(name).put(value); - } finally { - wl.unlock(); - } - } - - private void removeFrom(String name, Object value) { - wl.lock(); - hashCode = 0; - try { - if (map.containsKey(name)) - map.get(name).remove(value); - } finally { - wl.unlock(); - } - } - - @Override - public int hashCode() { - rl.lock(); - try { - if (hashCode == 0) { - HashCode c = HashCode.create(); - for (Property p : map.values()) - c.add(p); - this.hashCode = c.get(); - } - return hashCode; - } finally { - rl.unlock(); - } - } - - @Override - public boolean equals(Object o) { - rl.lock(); - try { - if (o instanceof PropertyMap) { - PropertyMap m = (PropertyMap)o; - if (m.hashCode() != hashCode()) - return false; - return this.map.equals(m.map); - } - return false; - } finally { - rl.unlock(); - } - } - - @Override - public String toString() { - ObjectMap m = new ObjectMap(); - m.put("id", System.identityHashCode(this)); - m.put("hashcode", hashCode()); - m.put("values", map); - return JsonSerializer.DEFAULT_LAX.toString(m); - } - } - - private abstract static class Property implements Comparable<Property> { - private final String name, type; - private final Object value; - - private static Property create(String name, Object value) { - if (name.endsWith(".set")) - return new SetProperty(name, value); - else if (name.endsWith(".list")) - return new ListProperty(name, value); - else if (name.endsWith(".map")) - return new MapProperty(name, value); - return new SimpleProperty(name, value); - } - - Property(String name, String type, Object value) { - this.name = name; - this.type = type; - this.value = value; - } - - void add(Object val) { - throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).", JsonSerializer.DEFAULT_LAX.toString(val), ClassUtils.getReadableClassNameForObject(val), name, type); - } - - void remove(Object val) { - throw new ConfigException("Cannot remove value {0} ({1}) from property ''{2}'' ({3}).", JsonSerializer.DEFAULT_LAX.toString(val), ClassUtils.getReadableClassNameForObject(val), name, type); - } - - void put(Object val) { - throw new ConfigException("Cannot put value {0} ({1}) to property ''{2}'' ({3}).", JsonSerializer.DEFAULT_LAX.toString(val), ClassUtils.getReadableClassNameForObject(val), name, type); - } - - void put(Object key, Object val) { - throw new ConfigException("Cannot put value {0}({1})->{2}({3}) to property ''{4}'' ({5}).", JsonSerializer.DEFAULT_LAX.toString(key), ClassUtils.getReadableClassNameForObject(key), JsonSerializer.DEFAULT_LAX.toString(val), ClassUtils.getReadableClassNameForObject(val), name, type); - } - - protected Object value() { - return value; - } - - @Override /* Object */ - public int hashCode() { - HashCode c = HashCode.create().add(name); - if (value instanceof Map) { - for (Map.Entry<?,?> e : ((Map<?,?>)value).entrySet()) - c.add(ContextFactory.toString(e.getKey())).add(ContextFactory.toString(e.getValue())); - } else if (value instanceof Collection) { - for (Object o : (Collection<?>)value) - c.add(ContextFactory.toString(o)); - } else { - c.add(ContextFactory.toString(value)); - } - return c.get(); - } - - @Override /* Object */ - public boolean equals(Object o) { - if (o instanceof Property) { - Property p = (Property)o; - return ContextFactory.same(value, p.value); - } - return false; - } - - @Override - public int compareTo(Property p) { - return name.compareTo(p.name); - } - - @Override - public String toString() { - return JsonSerializer.DEFAULT_LAX.toString(value); - } - } - - private static class SimpleProperty extends Property { - - SimpleProperty(String name, Object value) { - super(name, "SIMPLE", value); - } - } - - @SuppressWarnings({"unchecked"}) - private static class SetProperty extends Property { - private final Set<Object> value; - - private SetProperty(String name, Object value) { - super(name, "SET", new ConcurrentSkipListSet<Object>(PROPERTY_COMPARATOR)); - this.value = (Set<Object>)value(); - add(value); - } - - @Override - void add(Object val) { - if (val.getClass().isArray()) - for (int i = 0; i < Array.getLength(val); i++) - add(Array.get(val, i)); - else if (val instanceof Collection) - for (Object o : (Collection<Object>)val) - add(o); - else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - add(new ObjectList(s)); - return; - } catch (Exception e) {} - } - for (Object o : value) - if (same(val, o)) - return; - value.add(val); - } - } - - @Override - void remove(Object val) { - if (val.getClass().isArray()) - for (int i = 0; i < Array.getLength(val); i++) - remove(Array.get(val, i)); - else if (val instanceof Collection) - for (Object o : (Collection<Object>)val) - remove(o); - else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - remove(new ObjectList(s)); - return; - } catch (Exception e) {} - } - for (Iterator<Object> i = value.iterator(); i.hasNext();) - if (same(i.next(), val)) - i.remove(); - } - } - } - - @SuppressWarnings({"unchecked"}) - private static class ListProperty extends Property { - private final LinkedList<Object> value; - - private ListProperty(String name, Object value) { - super(name, "LIST", new LinkedList<Object>()); - this.value = (LinkedList<Object>)value(); - add(value); - } - - @Override - void add(Object val) { - if (val.getClass().isArray()) { - for (int i = Array.getLength(val) - 1; i >= 0; i--) - add(Array.get(val, i)); - } else if (val instanceof List) { - List<Object> l = (List<Object>)val; - for (ListIterator<Object> i = l.listIterator(l.size()); i.hasPrevious();) - add(i.previous()); - } else if (val instanceof Collection) { - List<Object> l = new ArrayList<Object>((Collection<Object>)val); - for (ListIterator<Object> i = l.listIterator(l.size()); i.hasPrevious();) - add(i.previous()); - } else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - add(new ObjectList(s)); - return; - } catch (Exception e) {} - } - for (Iterator<Object> i = value.iterator(); i.hasNext(); ) - if (same(val, i.next())) - i.remove(); - value.addFirst(val); - } - } - - @Override - void remove(Object val) { - if (val.getClass().isArray()) - for (int i = 0; i < Array.getLength(val); i++) - remove(Array.get(val, i)); - else if (val instanceof Collection) - for (Object o : (Collection<Object>)val) - remove(o); - else { - String s = val.toString(); - if (s.startsWith("[") && s.endsWith("]")) { - try { - remove(new ObjectList(s)); - return; - } catch (Exception e) {} - } - for (Iterator<Object> i = value.iterator(); i.hasNext();) - if (same(i.next(), val)) - i.remove(); - } - } - } - - @SuppressWarnings({"unchecked","rawtypes"}) - private static class MapProperty extends Property { - final Map<Object,Object> value; - - MapProperty(String name, Object value) { - // ConcurrentSkipListMap doesn't support Map.Entry.remove(), so use TreeMap instead. - super(name, "MAP", Collections.synchronizedMap(new TreeMap<Object,Object>(PROPERTY_COMPARATOR))); - this.value = (Map<Object,Object>)value(); - put(value); - } - - @Override - void put(Object val) { - try { - if (BeanContext.DEFAULT != null && ! (val instanceof Map)) - val = BeanContext.DEFAULT.convertToType(val, Map.class); - if (val instanceof Map) { - Map m = (Map)val; - for (Map.Entry e : (Set<Map.Entry>)m.entrySet()) - put(e.getKey(), e.getValue()); - return; - } - } catch (Exception e) {} - super.put(val); - } - - @Override - void put(Object key, Object val) { - // ConcurrentSkipListMap doesn't support Map.Entry.remove(). - for (Map.Entry<Object,Object> e : value.entrySet()) { - if (same(e.getKey(), key)) { - e.setValue(val); - return; - } - } - value.put(key, val); - } - } - - private static String toString(Object o) { - if (o instanceof Class) - return ((Class<?>)o).getName(); - return o.toString(); - } - - /* - * Compares two objects for "string"-equality. - * Basically mean both objects are equal if they're the same when converted to strings. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static boolean same(Object o1, Object o2) { - if (o1 == o2) - return true; - if (o1 instanceof Map) { - if (o2 instanceof Map) { - Map m1 = (Map)o1, m2 = (Map)o2; - if (m1.size() == m2.size()) { - Set<Map.Entry> s1 = m1.entrySet(), s2 = m2.entrySet(); - for (Iterator<Map.Entry> i1 = s1.iterator(), i2 = s2.iterator(); i1.hasNext();) { - Map.Entry e1 = i1.next(), e2 = i2.next(); - if (! same(e1.getKey(), e2.getKey())) - return false; - if (! same(e1.getValue(), e2.getValue())) - return false; - } - return true; - } - } - return false; - } else if (o1 instanceof Collection) { - if (o2 instanceof Collection) { - Collection c1 = (Collection)o1, c2 = (Collection)o2; - if (c1.size() == c2.size()) { - for (Iterator i1 = c1.iterator(), i2 = c2.iterator(); i1.hasNext();) { - if (! same(i1.next(), i2.next())) - return false; - } - return true; - } - } - return false; - } else { - return ContextFactory.toString(o1).equals(ContextFactory.toString(o2)); - } - } - - private String prefix(String name) { - if (name == null) - throw new ConfigException("Invalid property name specified: 'null'"); - if (name.indexOf('.') == -1) - return ""; - return name.substring(0, name.indexOf('.')); - } - - private String className(Object o) { - if (o == null) - return null; - if (o instanceof Class) - return ClassUtils.getReadableClassName((Class<?>)o); - return ClassUtils.getReadableClassName(o.getClass()); - } - - @Override /* Object */ - public String toString() { - rl.lock(); - try { - ObjectMap m = new ObjectMap(); - m.put("id", System.identityHashCode(this)); - m.put("hashCode", hashCode()); - m.put("properties.id", System.identityHashCode(properties)); - m.put("contexts.id", System.identityHashCode(contexts)); - m.put("properties", properties); - m.put("contexts", contexts); - return m.toString(); - } finally { - rl.unlock(); - } - } - - /** - * Creates an unlocked clone of this object. - * - * @throws CloneNotSupportedException If class cannot be cloned. - */ - @Override /* Object */ - public ContextFactory clone() throws CloneNotSupportedException { - rl.lock(); - try { - return new ContextFactory(this); - } finally { - rl.unlock(); - } - } -}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/CoreApi.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/CoreApi.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/CoreApi.java deleted file mode 100644 index 34e6429..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/CoreApi.java +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -/** - * Common super class for all core-API serializers, parsers, and serializer/parser groups. - * - * <h6 class='topic'>Description</h6> - * <p> - * Maintains an inner {@link ContextFactory} 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 ContextFactory} 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 { - - private ContextFactory contextFactory = ContextFactory.create(); - private BeanContext beanContext; - - /** - * Returns the {@link ContextFactory} object associated with this class. - * <p> - * The context factory stores all configuration properties for this class. - * Adding/modifying properties on this factory will alter the behavior of this object. - * <p> - * Calling the {@link ContextFactory#lock()} method on the returned object will prevent - * any further modifications to the configuration for this object - * ANY ANY OTHERS THAT SHARE THE SAME FACTORY!. - * Note that calling the {@link #lock()} method on this class will only - * lock the configuration for this particular instance of the class. - * - * @return The context factory associated with this object. - */ - public ContextFactory getContextFactory() { - return contextFactory; - } - - /** - * Returns the bean context to use for this class. - * - * @return The bean context object. - */ - public BeanContext getBeanContext() { - if (beanContext == null) - return contextFactory.getContext(BeanContext.class); - return beanContext; - } - - /** - * Creates a {@link Context} class instance of the specified type. - * - * @param contextClass The class instance to create. - * @return A context class instance of the specified type. - */ - protected final <T extends Context> T getContext(Class<T> contextClass) { - return contextFactory.getContext(contextClass); - } - - /** - * Shortcut for calling <code>getContextFactory().setProperty(<jf>property</jf>, <jf>value</jf>);</code>. - * - * @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 or {@link ContextFactory} object. - * @see ContextFactory#setProperty(String, Object) - */ - public CoreApi setProperty(String property, Object value) throws LockedException { - checkLock(); - contextFactory.setProperty(property, value); - return this; - } - - /** - * Shortcut for calling <code>getContextFactory().setProperties(<jf>properties</jf>);</code>. - * - * @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. - * @see ContextFactory#setProperties(java.util.Map) - */ - public CoreApi setProperties(ObjectMap properties) throws LockedException { - checkLock(); - contextFactory.setProperties(properties); - return this; - } - - /** - * Shortcut for calling <code>getContextFactory().addNotBeanClasses(<jf>classes</jf>)</code>. - * - * @see ContextFactory#addToProperty(String,Object) - * @param classes The new setting value for the bean context. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @return This object (for method chaining). - * @see ContextFactory#addToProperty(String, Object) - * @see BeanContext#BEAN_notBeanClasses - */ - public CoreApi addNotBeanClasses(Class<?>...classes) throws LockedException { - checkLock(); - contextFactory.addNotBeanClasses(classes); - return this; - } - - /** - * Shortcut for calling <code>getContextFactory().addTransforms(<jf>classes</jf>)</code>. - * - * @param classes The new setting value for the bean context. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @return This object (for method chaining). - * @see ContextFactory#addToProperty(String, Object) - * @see BeanContext#BEAN_transforms - */ - public CoreApi addTransforms(Class<?>...classes) throws LockedException { - checkLock(); - contextFactory.addTransforms(classes); - return this; - } - - /** - * Shortcut for calling <code>getContextFactory().addImplClass(<jf>interfaceClass</jf>, <jf>implClass</jf>)</code>. - * - * @param interfaceClass The interface class. - * @param implClass The implementation class. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @param <T> The class type of the interface. - * @return This object (for method chaining). - * @see ContextFactory#putToProperty(String, Object, Object) - * @see BeanContext#BEAN_implClasses - */ - public <T> CoreApi addImplClass(Class<T> interfaceClass, Class<? extends T> implClass) throws LockedException { - checkLock(); - contextFactory.addImplClass(interfaceClass, implClass); - return this; - } - - /** - * Shortcut for calling <code>getContextFactory().setClassLoader(<jf>classLoader</jf>)</code>. - * - * @param classLoader The new classloader. - * @throws LockedException If {@link ContextFactory#lock()} was called on this class or the bean context. - * @return This object (for method chaining). - * @see ContextFactory#setClassLoader(ClassLoader) - */ - public CoreApi setClassLoader(ClassLoader classLoader) throws LockedException { - checkLock(); - contextFactory.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() { - try { - super.lock(); - contextFactory = contextFactory.clone(); - contextFactory.lock(); - beanContext = contextFactory.getContext(BeanContext.class); - return this; - } catch (CloneNotSupportedException e) { - throw new RuntimeException(e); - } - } - - @Override /* Lockable */ - public CoreApi clone() throws CloneNotSupportedException { - CoreApi c = (CoreApi)super.clone(); - c.contextFactory = ContextFactory.create(contextFactory); - c.beanContext = null; - return c; - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/Delegate.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/Delegate.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/Delegate.java deleted file mode 100644 index dff2d66..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/Delegate.java +++ /dev/null @@ -1,33 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -/** - * 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/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedException.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedException.java deleted file mode 100644 index add0a79..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedException.java +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -import java.text.*; - -/** - * Subclass of non-runtime exceptions that take in a message and zero or more arguments. - * <p> - * - * @author James Bognar ([email protected]) - */ -public class FormattedException extends Exception { - - private static final long serialVersionUID = 1L; - - /** - * Constructor. - * - * @param message The {@link MessageFormat}-style message. - * @param args The arguments in the message. - */ - public FormattedException(String message, Object...args) { - super(args.length == 0 ? message : MessageFormat.format(message, args)); - } - - /** - * Constructor. - * - * @param causedBy The cause of this exception. - * @param message The {@link MessageFormat}-style message. - * @param args The arguments in the message. - */ - public FormattedException(Throwable causedBy, String message, Object...args) { - this(message, args); - initCause(causedBy); - } - - /** - * Constructor. - * - * @param causedBy The cause of this exception. - */ - public FormattedException(Throwable causedBy) { - this(causedBy, causedBy.getLocalizedMessage()); - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedRuntimeException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedRuntimeException.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedRuntimeException.java deleted file mode 100644 index f010d9f..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/FormattedRuntimeException.java +++ /dev/null @@ -1,47 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -import java.text.*; - -/** - * Subclass of runtime exceptions that take in a message and zero or more arguments. - * - * @author James Bognar ([email protected]) - */ -public class FormattedRuntimeException extends RuntimeException { - - private static final long serialVersionUID = 1L; - - /** - * Constructor. - * - * @param message The {@link MessageFormat}-style message. - * @param args The arguments in the message. - */ - public FormattedRuntimeException(String message, Object...args) { - super(args.length == 0 ? message : MessageFormat.format(message, args)); - } - - /** - * Constructor. - * - * @param causedBy The cause of this exception. - * @param message The {@link MessageFormat}-style message. - * @param args The arguments in the message. - */ - public FormattedRuntimeException(Throwable causedBy, String message, Object...args) { - this(message, args); - initCause(causedBy); - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/InvalidDataConversionException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/InvalidDataConversionException.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/InvalidDataConversionException.java deleted file mode 100644 index 21d0e57..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/InvalidDataConversionException.java +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -import java.text.*; - -import org.apache.juneau.internal.*; -import org.apache.juneau.json.*; - -/** - * 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}.", ClassUtils.getReadableClassNameForObject(value), ClassUtils.getReadableClassName(toType), getValue(value)), cause); - } - - /** - * @param toType Attempting to convert to this class type. - * @param cause The cause. - * @param value The value being converted. - */ - public InvalidDataConversionException(Object value, ClassMeta<?> toType, Exception cause) { - super(MessageFormat.format("Invalid data conversion from type ''{0}'' to type ''{1}''. Value={2}.", ClassUtils.getReadableClassNameForObject(value), toType.toString(), getValue(value)), cause); - } - - private static String getValue(Object o) { - if (o instanceof Class) - return "'" + ClassUtils.getReadableClassName((Class<?>)o) + "'"; - return JsonSerializer.DEFAULT_LAX == null ? "'" + o.toString() + "'" : JsonSerializer.DEFAULT_LAX.toString(o); - } -} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/Lockable.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/Lockable.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/Lockable.java deleted file mode 100644 index 4d3d470..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/Lockable.java +++ /dev/null @@ -1,88 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -/** - * 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/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/LockedException.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/LockedException.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/LockedException.java deleted file mode 100644 index 4d841f5..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/LockedException.java +++ /dev/null @@ -1,32 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -/** - * 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/2c3a7cb5/com.ibm.team.juno/src/main/java/org/apache/juneau/MediaRange.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno/src/main/java/org/apache/juneau/MediaRange.java b/com.ibm.team.juno/src/main/java/org/apache/juneau/MediaRange.java deleted file mode 100644 index 788243d..0000000 --- a/com.ibm.team.juno/src/main/java/org/apache/juneau/MediaRange.java +++ /dev/null @@ -1,316 +0,0 @@ -/*************************************************************************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - ***************************************************************************************************************************/ -package org.apache.juneau; - -import java.util.*; -import java.util.Map.*; - -/** - * 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; - } -}
