http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java new file mode 100755 index 0000000..1a70876 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/ObjectMap.java @@ -0,0 +1,1282 @@ +/******************************************************************************* + * 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 static com.ibm.juno.core.utils.ClassUtils.*; + +import java.io.*; +import java.util.*; + +import com.ibm.juno.core.filter.*; +import com.ibm.juno.core.filters.*; +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 object. + * <p> + * An extension of {@link LinkedHashMap}, so all methods available 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 java.util.Map} 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 object string directly into a {@link Map}. 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 Map</jc> + * Map m = <jk>new</jk> ObjectMap(); + * + * <jc>// Construct a Map from JSON</jc> + * String json = <js>"{a:'A',b:{c:'C',d:123}}"</js>; + * m = <jk>new</jk> ObjectMap(json); + * + * <jc>// Construct a Map using the append method</jc> + * m = <jk>new</jk> ObjectMap().append(<js>"foo"</js>,<js>"x"</js>).append(<js>"bar"</js>,123).append(<js>"baz"</js>,<jk>true</jk>); + * + * <jc>// Construct a Map from XML generated by XmlSerializer</jc> + * String xml = <js>"<object><a type='string'>A</a><b type='object'><c type='string'>C</c><d type='number'>123</d></b></object>"</js>; + * m = <jk>new</jk> ObjectMap(xml, DataFormat.<jsf>XML</jsf>); + * m = (Map)XmlParser.<jsf>DEFAULT</jsf>.parse(xml); <jc>// Equivalent</jc> + * m = (Map)XmlParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * m = XmlParser.<jsf>DEFAULT</jsf>.parse(Map.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * m = XmlParser.<jsf>DEFAULT</jsf>.parse(ObjectMap.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * + * <jc>// Construct a Map from a URL GET parameter string generated by UrlEncodingParser</jc> + * String urlParams = <js>"?a='A'&b={c:'C',d:123}"</js>; + * m = <jk>new</jk> ObjectMap(urlParams, DataFormat.<jsf>URLPARAM</jsf>); + * m = (Map)UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(Object.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * m = UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(Map.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * m = UrlEncodingParser.<jsf>DEFAULT</jsf>.parse(ObjectMap.<jk>class</jk>, xml); <jc>// Equivalent</jc> + * + * <jc>// Construct JSON from ObjectMap</jc> + * m = <jk>new</jk> ObjectMap(<js>"{foo:'bar'},{baz:[123,true]}"</js>); + * json = m.toString(); <jc>// Produces "{foo:'bar'},{baz:[123,true]}"</jc> + * json = m.toString(JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>); <jc>// Equivalent</jc> + * json = JsonSerializer.<jsf>DEFAULT_CONDENSED</jsf>.serialize(m); <jc>// Equivalent</jc> + * + * <jc>// Get a map entry as an Integer</jc> + * m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>); + * Integer i = m.getInt(<js>"foo"</js>); + * i = m.get(Integer.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> + * + * <jc>// Get a map entry as a Float</jc> + * m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>); + * Float f = m.getFloat(<js>"foo"</js>); + * f = m.get(Float.<jk>class</jk>, <js>"foo"</js>); <jc>// Equivalent</jc> + * + * <jc>// Same as above, except converted to a String</jc> + * m = <jk>new</jk> ObjectMap(<js>"{foo:123}"</js>); + * String s = m.getString(<js>"foo"</js>); <jc>// Returns "123"</jc> + * s = m.get(String.<jk>class</jk>, <js>"foo"</js>); <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> + * m = <jk>new</jk> ObjectMap(<js>"{person:{name:'John Smith',age:45}}"</js>); + * Person p = m.get(Person.<jk>class</jk>, <js>"person"</js>); + * + * <jc>// Add an inner map</jc> + * ObjectMap m1 = <jk>new</jk> ObjectMap(<js>"{a:1}"</js>); + * ObjectMap m2 = <jk>new</jk> ObjectMap(<js>"{b:2}"</js>).setInner(m1); + * <jk>int</jk> a = m2.getInt(<js>"a"</js>); <jc>// a == 1 </jc> + * </p> + * + * @author James Bognar ([email protected]) + */ +public class ObjectMap extends LinkedHashMap<String,Object> { + private static final long serialVersionUID = 1L; + + private transient BeanContext beanContext = BeanContext.DEFAULT; + private ObjectMap inner; + + /** + * An empty read-only ObjectMap. + */ + public static final ObjectMap EMPTY_MAP = new ObjectMap() { + + private static final long serialVersionUID = 1L; + + @Override /* Map */ + @SuppressWarnings("unchecked") + public Set<Map.Entry<String,Object>> entrySet() { + return Collections.EMPTY_MAP.entrySet(); + } + + @Override /* Map */ + @SuppressWarnings("unchecked") + public Set<String> keySet() { + return Collections.EMPTY_MAP.keySet(); + } + + @Override /* Map */ + public Object put(String key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override /* Map */ + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + @Override /* Map */ + public Collection<Object> values() { + return Collections.emptyMap().values(); + } + }; + + /** + * Construct an ObjectMap directly from a string 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 ObjectMap(CharSequence s, ReaderParser p) throws ParseException { + this(p == null ? BeanContext.DEFAULT : p.getBeanContext()); + try { + if (p == null) + p = JsonParser.DEFAULT; + if (s != null) + p.parseIntoMap(new CharSequenceReader(s), s.length(), this, beanContext.string(), beanContext.object()); + } catch (IOException e) { + throw new ParseException(e); + } + } + + /** + * Shortcut for <code><jk>new</jk> ObjectMap(string,JsonParser.<jsf>DEFAULT</jsf>);</code> + * + * @param s The JSON text to parse. + * @throws ParseException If the input contains a syntax error or is malformed. + */ + public ObjectMap(CharSequence s) throws ParseException { + this(s, null); + } + + /** + * Construct an ObjectMap directly from a reader using the specified parser. + * + * @param r The reader to read from. The reader will be wrapped in a {@link BufferedReader} if it isn't already. + * @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 ObjectMap(Reader r, ReaderParser p) throws ParseException, IOException { + parseReader(r, p); + } + + /** + * Shortcut for <code><jk>new</jk> ObjectMap(reader, JsonParser.<jsf>DEFAULT</jsf>)</code>. + * + * @param r The reader to read from. The reader will be wrapped in a {@link BufferedReader} if it isn't already. + * @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 ObjectMap(Reader r) throws ParseException, IOException { + parseReader(r, JsonParser.DEFAULT); + } + + private void parseReader(Reader r, ReaderParser p) throws IOException, ParseException { + if (p == null) + p = JsonParser.DEFAULT; + p.parseIntoMap(r, -1, this, beanContext.string(), beanContext.object()); + } + + /** + * Construct an empty JSON object (i.e. an empty {@link LinkedHashMap}). + */ + public ObjectMap() { + this(BeanContext.DEFAULT); + } + + /** + * Construct an empty JSON object (i.e. an empty {@link LinkedHashMap}) with the specified bean context. + * + * @param beanContext The bean context to use for creating beans. + */ + public ObjectMap(BeanContext beanContext) { + super(); + this.beanContext = beanContext; + } + + /** + * Construct a JSON object and fill it with the contents from the specified {@link Map}. + * + * @param m The map whose entries will be copied into this map. + */ + public ObjectMap(Map<?,?> m) { + super(); + for (Map.Entry<?,?> e : m.entrySet()) + put(e.getKey().toString(), e.getValue()); + } + + /** + * Set an inner map in this map to allow for chained get calls. + * <p> + * If {@link #get(Object)} returns <jk>null</jk>, then {@link #get(Object)} will be called on the inner map. + * <p> + * In addition to providing the ability to chain maps, this method also provides the ability + * to wrap an existing map inside another map so that you can add entries to the outer + * map without affecting the values on the inner map. + * <p class='bcode'> + * ObjectMap m1 = <jk>new</jk> ObjectMap(<js>"{foo:1}"</js>); + * ObjectMap m2 = <jk>new</jk> ObjectMap().setInner(m1); + * m2.put(<js>"foo"</js>, 2); <jc>// Overwrite the entry</jc> + * <jk>int</jk> foo1 = m1.getInt(<js>"foo"</js>); <jc>// foo1 == 1 </jc> + * <jk>int</jk> foo2 = m2.getInt(<js>"foo"</js>); <jc>// foo2 == 2 </jc> + * </p> + * + * @param inner The inner map. + * Can be <jk>null</jk> to remove the inner map from an existing map. + * @return This object (for method chaining). + */ + public ObjectMap setInner(ObjectMap inner) { + this.inner = inner; + return this; + } + + /** + * Searches for the specified key in this map ignoring case. + * + * @param key The key to search for. For performance reasons, it's preferrable that the key be all lowercase. + * @return The key, or <jk>null</jk> if map does not contain this key. + */ + public String findKeyIgnoreCase(String key) { + for (String k : keySet()) + if (key.equalsIgnoreCase(k)) + return k; + return null; + } + + + /** + * Returns the inner map if one was set through {@link #setInner(ObjectMap)}. + * + * @return The inner map if one was set through {@link #setInner(ObjectMap)}, or <jk>null</jk> if no inner map is present. + */ + public ObjectMap getInner() { + return inner; + } + + /** + * 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 ObjectMap setBeanContext(BeanContext beanContext) { + this.beanContext = beanContext; + return this; + } + + /** + * Returns the {@link BeanContext} currently associated with this map. + * + * @return The {@link BeanContext} currently associated with this map. + */ + public BeanContext getBeanContext() { + return beanContext; + } + + /** + * Convenience method for adding multiple objects to this map. + * <p> + * Equivalent to calling {@code put(key, value)}, but returns + * this map so that the method can be chained. + * + * @param key The key. + * @param value The value. + * @return This object (for method chaining). + */ + public ObjectMap append(String key, Object value) { + put(key, value); + return this; + } + + @Override /* Map */ + public Object get(Object key) { + Object o = super.get(key); + if (o == null && inner != null) + o = inner.get(key); + return o; + } + + /** + * Same as {@link Map#get(Object) get()}, but returns the default value if the key + * could not be found. + * + * @param key The key. + * @param def The default value if the entry doesn't exist. + * @return The value, or the default value if the entry doesn't exist. + */ + public Object get(String key, Object def) { + Object o = get(key); + return (o == null ? def : o); + } + + /** + * Same as {@link Map#get(Object) get()}, but casts or converts the value to the specified class type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param <T> The class type. + * @param type The class type. + * @param key The key. + * @return The value, or <jk>null</jk> if the entry doesn't exist. + */ + public <T> T get(Class<T> type, String key) { + return get(type, key, null); + } + + /** + * Same as {@link Map#get(Object) get()}, but converts the raw value to the specified class type using the specified filter. + * + * @param <T> The filtered class type. + * @param filter The filter class used to convert the raw type to a filtered type. + * @param key The key. + * @return The value, or <jk>null</jk> if the entry doesn't exist. + * @throws ParseException Thrown by the filter if a problem occurred trying to parse the value. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public <T> T get(PojoFilter<T,?> filter, String key) throws ParseException { + Object o = super.get(key); + if (o == null) + return null; + PojoFilter f = filter; + return (T)f.unfilter(o, null); + } + + /** + * Same as {@link Map#get(Object) get()}, but casts or converts the value to the specified class type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param <T> The class type. + * @param type The class type. + * @param key The key. + * @param def The default value if the entry doesn't exist. + * @return The value, or the default value if the entry doesn't exist. + */ + public <T> T get(Class<T> type, String key, T def) { + Object o = get(key); + if (o == null) + return def; + T t = beanContext.convertToType(o, type); + if (t == null) + return def; + return t; + } + + /** + * Same as {@link Map#get(Object) get()}, but casts or converts the value to the specified class type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param <T> The class type. + * @param type The class type. + * @param key The key. + * @return The value, or the default value if the entry doesn't exist. + */ + public <T> T get(ClassMeta<T> type, String key) { + return get(type, key, null); + } + + /** + * Same as {@link Map#get(Object) get()}, but casts or converts the value to the specified class type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param <T> The class type. + * @param type The class type. + * @param key The key. + * @param def The default value if the entry doesn't exist. + * @return The value, or the default value if the entry doesn't exist. + */ + public <T> T get(ClassMeta<T> type, String key, T def) { + Object o = get(key); + if (o == null) + return def; + return beanContext.convertToType(o, type); + } + + /** + * Returns the value for the first key in the list that has an entry in this map. + * + * @param keys The keys to look up in order. + * @return The value of the first entry whose key exists, or <jk>null</jk> if none of the keys exist in this map. + */ + public Object find(String...keys) { + for (String key : keys) + if (containsKey(key)) + return get(key); + return null; + } + + /** + * Returns the value for the first key in the list that has an entry in this map. + * <p> + * Casts or converts the value to the specified class type. + * <p> + * See {@link BeanContext#convertToType(Object, ClassMeta)} for the list of valid data conversions. + * + * @param type The class type to convert the value to. + * @param <T> The class type to convert the value to. + * @param keys The keys to look up in order. + * @return The value of the first entry whose key exists, or <jk>null</jk> if none of the keys exist in this map. + */ + public <T> T find(Class<T> type, String...keys) { + for (String key : keys) + if (containsKey(key)) + return get(type, key); + return null; + } + + /** + * Convenience method for inserting JSON directly into an attribute on this object. + * <p> + * The JSON text can be an object (i.e. <js>"{...}"</js>) or an array (i.e. <js>"[...]"</js>). + * + * @param key The key. + * @param json The JSON text that will be parsed into an Object and then inserted into this map. + * @throws ParseException If the input contains a syntax error or is malformed. + */ + public void putJson(String key, String json) throws ParseException { + this.put(key, JsonParser.DEFAULT.parse(json, Object.class)); + } + + /** + * Returns the specified entry value converted to a {@link String}. + * <p> + * Shortcut for <code>get(String.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + */ + public String getString(String key) { + return get(String.class, key); + } + + /** + * Specialized method that calls {@link #getString(String)} and splits the + * results as a simple comma-delimited list. + * + * @param key the key. + * @return A list of tokens, trimmed of whitespace. An empty list if entry not found. Never <jk>null</jk>. + */ + public String[] getStringArray(String key) { + String s = get(String.class, key); + return (s == null ? new String[0] : StringUtils.split(s, ',')); + } + + /** + * Same as {@link #getStringArray(String)} but returns a default value if the value cannot be found. + * + * @param key The map key. + * @param def The default value if value is not found. + * @return The value converted to a string array. + */ + public String[] getStringArray(String key, String[] def) { + String s = get(String.class, key); + String[] r = (s == null ? new String[0] : StringUtils.split(s, ',')); + return (r.length == 0 ? def : r); + } + + /** + * Returns the specified entry value converted to a {@link String}. + * <p> + * Shortcut for <code>get(String.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + */ + public String getString(String key, String defVal) { + return get(String.class, key, defVal); + } + + /** + * Returns the specified entry value converted to an {@link Integer}. + * <p> + * Shortcut for <code>get(Integer.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Integer getInt(String key) { + return get(Integer.class, key); + } + + /** + * Returns the specified entry value converted to an {@link Integer}. + * <p> + * Shortcut for <code>get(Integer.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Integer getInt(String key, Integer defVal) { + return get(Integer.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link Long}. + * <p> + * Shortcut for <code>get(Long.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Long getLong(String key) { + return get(Long.class, key); + } + + /** + * Returns the specified entry value converted to a {@link Long}. + * <p> + * Shortcut for <code>get(Long.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Long getLong(String key, Long defVal) { + return get(Long.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link Boolean}. + * <p> + * Shortcut for <code>get(Boolean.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Boolean getBoolean(String key) { + return get(Boolean.class, key); + } + + /** + * Returns the specified entry value converted to a {@link Boolean}. + * <p> + * Shortcut for <code>get(Boolean.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Boolean getBoolean(String key, Boolean defVal) { + return get(Boolean.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link Map}. + * <p> + * Shortcut for <code>get(Map.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Map<?,?> getMap(String key) { + return get(Map.class, key); + } + + /** + * Returns the specified entry value converted to a {@link Map}. + * <p> + * Shortcut for <code>get(Map.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Map<?,?> getMap(String key, Map<?,?> defVal) { + return get(Map.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link List}. + * <p> + * Shortcut for <code>get(List.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public List<?> getList(String key) { + return get(List.class, key); + } + + /** + * Returns the specified entry value converted to a {@link List}. + * <p> + * Shortcut for <code>get(List.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public List<?> getList(String key, List<?> defVal) { + return get(List.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link Map}. + * <p> + * Shortcut for <code>get(ObjectMap.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectMap getObjectMap(String key) { + return get(ObjectMap.class, key); + } + + /** + * Returns the specified entry value converted to a {@link ObjectMap}. + * <p> + * Shortcut for <code>get(ObjectMap.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectMap getObjectMap(String key, ObjectMap defVal) { + return get(ObjectMap.class, key, defVal); + } + + /** + * Returns the specified entry value converted to a {@link ObjectList}. + * <p> + * Shortcut for <code>get(ObjectList.<jk>class</jk>, key)</code>. + * + * @param key The key. + * @return The converted value, or <jk>null</jk> if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectList getObjectList(String key) { + return get(ObjectList.class, key); + } + + /** + * Returns the specified entry value converted to a {@link ObjectList}. + * <p> + * Shortcut for <code>get(ObjectList.<jk>class</jk>, key, defVal)</code>. + * + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectList getObjectList(String key, ObjectList defVal) { + return get(ObjectList.class, key, defVal); + } + + /** + * Returns the first entry that exists converted to a {@link String}. + * <p> + * Shortcut for <code>find(String.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + */ + public String findString(String... keys) { + return find(String.class, keys); + } + + /** + * Returns the first entry that exists converted to an {@link Integer}. + * <p> + * Shortcut for <code>find(Integer.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Integer findInt(String... keys) { + return find(Integer.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link Long}. + * <p> + * Shortcut for <code>find(Long.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Long findLong(String... keys) { + return find(Long.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link Boolean}. + * <p> + * Shortcut for <code>find(Boolean.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Boolean findBoolean(String... keys) { + return find(Boolean.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link Map}. + * <p> + * Shortcut for <code>find(Map.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public Map<?,?> findMap(String... keys) { + return find(Map.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link List}. + * <p> + * Shortcut for <code>find(List.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public List<?> findList(String... keys) { + return find(List.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link ObjectMap}. + * <p> + * Shortcut for <code>find(ObjectMap.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectMap findObjectMap(String... keys) { + return find(ObjectMap.class, keys); + } + + /** + * Returns the first entry that exists converted to a {@link ObjectList}. + * <p> + * Shortcut for <code>find(ObjectList.<jk>class</jk>, keys)</code>. + * + * @param keys The list of keys to look for. + * @return The converted value of the first key in the list that has an entry in this map, + * or <jk>null</jk> if the map contains no mapping for any of the keys. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public ObjectList findObjectList(String... keys) { + return find(ObjectList.class, keys); + } + + /** + * Returns the first key in the map. + * + * @return The first key in the map, or <jk>null</jk> if the map is empty. + */ + public String getFirstKey() { + return isEmpty() ? null : keySet().iterator().next(); + } + + /** + * Returns the class type of the object at the specified index. + * + * @param key The key into this map. + * @return The data type of the object at the specified key, or <jk>null</jk> if the value is null or does not exist. + */ + public ClassMeta<?> getClassMeta(String key) { + return beanContext.getClassMetaForObject(get(key)); + } + + /** + * Equivalent to calling <code>get(class,key,def)</code> followed by <code>remove(key);</code> + * + * @param <T> The class type. + * @param type The class type. + * @param key The key. + * @param defVal The default value if the map doesn't contain the specified mapping. + * @return The converted value, or the default value if the map contains no mapping for this key. + * @throws InvalidDataConversionException If value cannot be converted. + */ + public <T> T remove(Class<T> type, String key, T defVal) { + T t = get(type, key, defVal); + remove(key); + return t; + } + + + /** + * Convenience method for removing several keys at once. + * + * @param keys The list of keys to remove. + */ + public void removeAll(Collection<String> keys) { + for (String k : keys) + remove(k); + } + + /** + * Convenience method for removing several keys at once. + * + * @param keys The list of keys to remove. + */ + public void removeAll(String... keys) { + for (String k : keys) + remove(k); + } + + @Override /* Map */ + public boolean containsKey(Object key) { + if (super.containsKey(key)) + return true; + if (inner != null) + return inner.containsKey(key); + return false; + } + + /** + * Returns <jk>true</jk> if this map contains the specified key, ignoring + * the inner map if it exists. + * + * @param key The key to look up. + * @return <jk>true</jk> if this map contains the specified key. + */ + public boolean containsOuterKey(Object key) { + return super.containsKey(key); + } + + /** + * Returns a copy of this <code>ObjectMap</code> with only the specified keys. + * + * @param keys The keys of the entries to copy. + * @return A new map with just the keys and values from this map. + */ + public ObjectMap include(String...keys) { + ObjectMap m2 = new ObjectMap(); + for (Map.Entry<String,Object> e : this.entrySet()) + for (String k : keys) + if (k.equals(e.getKey())) + m2.put(k, e.getValue()); + return m2; + } + + /** + * Returns a copy of this <code>ObjectMap</code> without the specified keys. + * + * @param keys The keys of the entries not to copy. + * @return A new map without the keys and values from this map. + */ + public ObjectMap exclude(String...keys) { + ObjectMap m2 = new ObjectMap(); + for (Map.Entry<String,Object> e : this.entrySet()) { + boolean exclude = false; + for (String k : keys) + if (k.equals(e.getKey())) + exclude = true; + if (! exclude) + m2.put(e.getKey(), e.getValue()); + } + return m2; + } + + /** + * Sets a value in this map if the entry does not exist or the value is <jk>null</jk>. + * + * @param key The map key. + * @param val The value to set if the current value does not exist or is <jk>null</jk>. + * @return This object (for method chaining). + */ + public ObjectMap putIfNull(String key, Object val) { + Object o = get(key); + if (o == null) + put(key, val); + return this; + } + + /** + * Sets a value in this map if the entry does not exist or the value is <jk>null</jk> or an empty string. + * + * @param key The map key. + * @param val The value to set if the current value does not exist or is <jk>null</jk> or an empty string. + * @return This object (for method chaining). + */ + public ObjectMap putIfEmpty(String key, Object val) { + Object o = get(key); + if (o == null || o.toString().isEmpty()) + put(key, val); + return this; + } + + /** + * Converts this map into the class type specified by the <js>"_class"</js> entry value. + * <p> + * This method can be used to convert <code>ObjectMap</code> objects to a variety of POJO types. + * + * <dl> + * <dt>Example of valid class types:</dt> + * <dd> + * <p> + * An object map can be converted to a bean. + * </p> + * <p class='bcode'> + * { + * _class: <js>'com.ibm.sample.addressBook.Person'</js>, + * name: <js>'John Smith'</js>, + * ... + * } + * </p> + * <p> + * It can also be converted into another map type. + * </p> + * <p class='bcode'> + * <jc>// Generic TreeMap (String keys, Object values)</jc> + * { + * _class: <js>'java.util.TreeMap'</js>, + * name: <js>'John Smith'</js>, + * ... + * } + * <jc>// TreeMap where values are forced to be strings.</jc> + * { + * _class: <js>'java.util.TreeMap<java.lang.String,java.lang.String>'</js>, + * name: <js>'John Smith'</js>, + * ... + * } + * </p> + * <p> + * It can also be converted to Collections objects if map defines an <code>items</code> entry of type array. + * </p> + * <p class='bcode'> + * <jc>// LinkedList of strings</jc> + * { + * _class: <js>'java.util.LinkedList'</js>, + * items: [ <js>'John Smith'</js>, ... ] + * } + * <jc>// LinkedList of beans</jc> + * { + * _class: <js>'java.util.LinkedList<com.ibm.sample.addressBook.Person>'</js>, + * items: [ { name: <js>'John Smith'</js>, ... }, ... ] + * } + * </p> + * <p> + * It can also be converted to arrays. + * </p> + * <p class='bcode'> + * <jc>// Array of strings</jc> + * { + * _class: <js>'java.lang.String[]'</js>, + * items: [ <js>'John Smith'</js>, ... ] + * } + * <jc>// Array of beans</jc> + * { + * _class: <js>'com.ibm.sample.addressBook.Person[]'</js>, + * items: [ { name: <js>'John Smith'</js>, ... }, ... ] + * } + * </p> + * <p> + * It can also be converted to any type that can be handled by the {@link BeanContext#convertToType(Object, Class)} method. + * In this case, the value is specified by an <code>value</code> entry of any type. + * For example, if the bean context has a {@link CalendarFilter} associated with it, it can convert a string value to a calendar. + * <p class='bcode'> + * { + * _class: <js>'java.util.GregorianCalendar'</js>, + * value: <js>'2001-07-04T15:30:45-05:00'</js> + * } + * </p> + * </dd> + * <dt>Notes:</dt> + * <dd> + * <ul> + * <li>This method is recursive. It will also recursively convert any descendant entries to POJOs. + * </ul> + * </dd> + * </dl> + * + * @return The new Java object of type specified by the <js>"_class"</js> entry value, or this + * same object if entry does not exist. + */ + public Object cast() { + String c = (String)get("_class"); + if (c == null) { + if (containsKey("_value")) + return get("_value"); + return this; + } + return cast2(beanContext.getClassMetaFromString(c)); + } + + /** + * Converts this map into an object of the specified type. + * <p> + * The rules are the same as those specified in {@link #cast()}. + * <p> + * If this map contains a <js>"_class"</js> entry, it must be the same as or a subclass + * of the <code>type</code>. + * + * @param <T> The class type to convert this map object to. + * @param type The class type to convert this map object to. + * @return The new object. + * @throws ClassCastException If the <js>"_class"</js> entry is present and not assignable + * from <code>type</code> + */ + @SuppressWarnings("unchecked") + public <T> T cast(Class<T> type) { + ClassMeta<?> c1 = beanContext.getClassMetaFromString((String)get("_class")); + ClassMeta<?> c2 = beanContext.getClassMeta(type); + ClassMeta<?> c = narrowClassMeta(c1, c2); + return (T)cast2(c); + } + + /** + * Same as {@link #cast(Class)}, except allows you to specify a {@link ClassMeta} parameter. + * + * @param <T> The class type to convert this map object to. + * @param cm The class type to convert this map object to. + * @return The new object. + * @throws ClassCastException If the <js>"_class"</js> entry is present and not assignable + * from <code>type</code> + */ + @SuppressWarnings({"unchecked"}) + public <T> T cast(ClassMeta<T> cm) { + ClassMeta<?> c1 = beanContext.getClassMetaFromString((String)get("_class")); + ClassMeta<?> c = narrowClassMeta(c1, cm); + return (T)cast2(c); + } + + /* + * Combines the class specified by a "_class" attribute with the ClassMeta + * passed in through the cast(ClassMeta) method. + * The rule is that child classes superceed parent classes, and c2 superceeds c1 + * if one isn't the parent of another. + */ + @SuppressWarnings("unchecked") + private ClassMeta<?> narrowClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) { + if (c1 == null) + return c2; + ClassMeta<?> c = getNarrowedClassMeta(c1, c2); + if (c1.isMap()) { + ClassMeta<?> k = getNarrowedClassMeta(c1.getKeyType(), c2.getKeyType()); + ClassMeta<?> v = getNarrowedClassMeta(c1.getValueType(), c2.getValueType()); + return beanContext.getMapClassMeta((Class<? extends Map<?,?>>)c.getInnerClass(), k, v); + } + if (c1.isCollection()) { + ClassMeta<?> e = getNarrowedClassMeta(c1.getElementType(), c2.getElementType()); + return beanContext.getCollectionClassMeta((Class<? extends Collection<?>>)c.getInnerClass(), e); + } + return c; + } + + /* + * If c1 is a child of c2 or the same as c2, returns c1. + * Otherwise, returns c2. + */ + private ClassMeta<?> getNarrowedClassMeta(ClassMeta<?> c1, ClassMeta<?> c2) { + if (isParentClass(c2.getInnerClass(), c1.getInnerClass())) + return c1; + return c2; + } + + /* + * Converts this map to the specified class type. + */ + @SuppressWarnings({"unchecked","rawtypes"}) + private <T> T cast2(ClassMeta<T> cm) { + + try { + Object value = get("value"); + + if (cm.isMap()) { + Map m2 = (cm.canCreateNewInstance() ? (Map)cm.newInstance() : new ObjectMap(beanContext)); + ClassMeta<?> kType = cm.getKeyType(), vType = cm.getValueType(); + for (Map.Entry<String,Object> e : entrySet()) { + Object k = e.getKey(); + Object v = e.getValue(); + if (! k.equals("_class")) { + + // Attempt to recursively cast child maps. + if (v instanceof ObjectMap) + v = ((ObjectMap)v).cast(); + + k = (kType.isString() ? k : beanContext.convertToType(k, kType)); + v = (vType.isObject() ? v : beanContext.convertToType(v, vType)); + + m2.put(k, v); + } + } + return (T)m2; + + } else if (cm.isBean()) { + BeanMap<? extends T> bm = beanContext.newBeanMap(cm.getInnerClass()); + + // Iterate through all the entries in the map and set the individual field values. + for (Map.Entry<String,Object> e : entrySet()) { + String k = e.getKey(); + Object v = e.getValue(); + if (! k.equals("_class")) { + + // Attempt to recursively cast child maps. + if (v instanceof ObjectMap) + v = ((ObjectMap)v).cast(); + + bm.put(k, v); + } + } + + return bm.getBean(); + + } else if (cm.isArray() || cm.isCollection()) { + List items = (List)get("items"); + return beanContext.convertToType(items, cm); + + } else if (value != null) { + return beanContext.convertToType(value, cm); + } + + } catch (Exception e) { + throw new BeanRuntimeException(cm.innerClass, "Error occurred attempting to cast to an object of type ''{0}''", cm.innerClass.getName()).initCause(e); + } + + throw new BeanRuntimeException(cm.innerClass, "Cannot convert to class type ''{0}''. Only beans and maps can be converted using this method.", cm.innerClass.getName()); + } + + + /** + * Serialize this object into a string using the specified serializer. + * + * @param serializer The serializer to use to convert this object to a string. + * @return This object serialized as a 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 object into a JSON string 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 map to the specified <code>Writer</code> using + * the {@link JsonSerializer#DEFAULT} serializer. + * + * @param w The writer to serialize this object to. + * @return This object (for method chaining). + * @throws IOException If a problem occurred trying to write to the writer. + * @throws SerializeException If a problem occurred trying to convert the output. + */ + public ObjectMap serializeTo(Writer w) throws IOException, SerializeException { + JsonSerializer.DEFAULT.serialize(this); + return this; + } + + @Override /* Map */ + public Set<String> keySet() { + if (inner == null) + return super.keySet(); + LinkedHashSet<String> s = new LinkedHashSet<String>(); + s.addAll(inner.keySet()); + s.addAll(super.keySet()); + return s; + } + + @Override /* Map */ + public Set<Map.Entry<String,Object>> entrySet() { + if (inner == null) + return super.entrySet(); + + final Set<String> keySet = keySet(); + final Iterator<String> keys = keySet.iterator(); + + return new AbstractSet<Map.Entry<String,Object>>() { + + @Override /* Iterable */ + public Iterator<Map.Entry<String,Object>> iterator() { + + return new Iterator<Map.Entry<String,Object>>() { + + @Override /* Iterator */ + public boolean hasNext() { + return keys.hasNext(); + } + + @Override /* Iterator */ + public Map.Entry<String,Object> next() { + return new Map.Entry<String,Object>() { + String key = keys.next(); + + @Override /* Map.Entry */ + public String getKey() { + return key; + } + + @Override /* Map.Entry */ + public Object getValue() { + return get(key); + } + + @Override /* Map.Entry */ + public Object setValue(Object object) { + return put(key, object); + } + }; + } + + @Override /* Iterator */ + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + @Override /* Set */ + public int size() { + return keySet.size(); + } + }; + } +}
http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.class new file mode 100755 index 0000000..2154b91 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.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/PropertyNamer.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java new file mode 100755 index 0000000..a4c26d3 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamer.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * 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; + +import com.ibm.juno.core.annotation.*; + +/** + * Defines an API for converting conventional bean property names to some other form. + * <p> + * For example, given the bean property <js>"fooBarURL"</js>, the {@link PropertyNamerDashedLC} + * property namer will convert this to <js>"foo-bar-url"</js>. + * <p> + * Property namers are associated with beans through the {@link Bean#propertyNamer} annotation. + * + * @author James Bognar ([email protected]) + */ +public interface PropertyNamer { + + /** + * Convert the specified default property name to some other value. + * @param name The original bean property name. + * @return The converted property name. + */ + public String getPropertyName(String name); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.class new file mode 100755 index 0000000..752a35e Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.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/PropertyNamerDashedLC.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java new file mode 100755 index 0000000..4d93c7e --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDashedLC.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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; + +/** + * Converts property names to dashed-lower-case format. + * <p> + * Examples: + * <ul> + * <li><js>"fooBar"</js> -> <js>"foo-bar"</js> + * <li><js>"fooBarURL"</js> -> <js>"foo-bar-url"</js> + * <li><js>"FooBarURL"</js> -> <js>"foo-bar-url"</js> + * </ul> + * + * @author James Bognar ([email protected]) + */ +public final class PropertyNamerDashedLC implements PropertyNamer { + + @Override /* PropertyNamer */ + public String getPropertyName(String name) { + if (name == null || name.isEmpty()) + return name; + + int numUCs = 0; + boolean isPrevUC = Character.isUpperCase(name.charAt(0)); + for (int i = 1; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isUpperCase(c)) { + if (! isPrevUC) + numUCs++; + isPrevUC = true; + } else { + isPrevUC = false; + } + } + + char[] name2 = new char[name.length() + numUCs]; + isPrevUC = Character.isUpperCase(name.charAt(0)); + name2[0] = Character.toLowerCase(name.charAt(0)); + int ni = 0; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (Character.isUpperCase(c)) { + if (! isPrevUC) + name2[ni++] = '-'; + isPrevUC = true; + name2[ni++] = Character.toLowerCase(c); + } else { + isPrevUC = false; + name2[ni++] = c; + } + } + + return new String(name2); + } +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.class new file mode 100755 index 0000000..3a6cb52 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.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/PropertyNamerDefault.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java new file mode 100755 index 0000000..c534d76 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/PropertyNamerDefault.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * 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; + +import java.beans.*; + +/** + * Default property namer. + * <p> + * Examples: + * <ul> + * <li><js>"fooBar"</js> -> <js>"fooBar"</js> + * <li><js>"fooBarURL"</js> -> <js>"fooBarURL"</js> + * <li><js>"FooBarURL"</js> -> <js>"fooBarURL"</js> + * <li><js>"URL"</js> -> <js>"URL"</js> + * </ul> + * <p> + * See {@link Introspector#decapitalize(String)} for exact rules. + * + * @author James Bognar ([email protected]) + */ +public final class PropertyNamerDefault implements PropertyNamer { + + @Override /* PropertyNamer */ + public String getPropertyName(String name) { + return Introspector.decapitalize(name); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.class new file mode 100755 index 0000000..7c7b7a5 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.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/Streamable.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java new file mode 100755 index 0000000..4dc8f2f --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Streamable.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core; + +import java.io.*; + +/** + * Interface that identifies that an object can be serialized directly to an output stream. + * <p> + * Instances must identify the media type of the content by implementing the + * {@link #getMediaType()} method. + * </p> + * + * @author James Bognar ([email protected]) + */ +public interface Streamable { + + /** + * Serialize this object to the specified output stream. + * + * @param os The output stream to stream to. + * @throws IOException + */ + void streamTo(OutputStream os) throws IOException; + + /** + * Returns the serialized media type for this resource (e.g. <js>"text/html"</js>). + * + * @return The media type, or <jk>null</jk> if the media type is not known. + */ + String getMediaType(); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$1.class new file mode 100755 index 0000000..2fb5745 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility$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/Visibility.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.class new file mode 100755 index 0000000..3bd640f Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.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/Visibility.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java new file mode 100755 index 0000000..0687d39 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Visibility.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2014, 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core; + +import java.lang.reflect.*; + +/** + * Defines class/field/method visibilities. + * <p> + * Used to specify minimum levels of visibility when detecting bean classes, methods, and fields. + * Used in conjunction with the following bean context properties: + * <ul> + * <li>{@link BeanContextProperties#BEAN_beanConstructorVisibility} + * <li>{@link BeanContextProperties#BEAN_beanClassVisibility} + * <li>{@link BeanContextProperties#BEAN_beanFieldVisibility} + * <li>{@link BeanContextProperties#BEAN_methodVisibility} + * </ul> + * + * @author James Bognar ([email protected]) + */ +public enum Visibility { + + /** Ignore all */ + NONE, + + /** Include only <jk>public</jk> classes/fields/methods. */ + PUBLIC, + + /** Include only <jk>public</jk> or <jk>protected</jk> classes/fields/methods. */ + PROTECTED, + + /** Include all but <jk>private</jk> classes/fields/methods. */ + DEFAULT, + + /** Include all classes/fields/methods. */ + PRIVATE; + + /** + * Identifies if the specified mod matches this visibility. + * Example: + * <code> + * <jsf>PUBLIC</jsf>.isVisible(MyPublicClass.<jk>class</jk>.getModifiers()); <jc>//true</jk> + * <jsf>PUBLIC</jsf>.isVisible(MyPrivateClass.<jk>class</jk>.getModifiers()); <jc>//false</jk> + * <jsf>PRIVATE</jsf>.isVisible(MyPrivateClass.<jk>class</jk>.getModifiers()); <jc>//true</jk> + * <jsf>NONE</jsf>.isVisible(MyPublicClass.<jk>class</jk>.getModifiers()); <jc>//false</jk> + * </code> + * + * @param mod The modifier from the object being tested (e.g. results from {@link Class#getModifiers()}. + * @return <jk>true</jk> if this visibility matches the specified modifier attribute. + */ + public boolean isVisible(int mod) { + switch(this) { + case NONE: return false; + case PRIVATE: return true; + case DEFAULT: return ! Modifier.isPrivate(mod); + case PROTECTED: return Modifier.isProtected(mod) || Modifier.isPublic(mod); + default: return Modifier.isPublic(mod); + } + } + + /** + * Shortcut for <code>isVisible(x.getModifiers());</code> + * + * @param x The constructor to check. + * @return <jk>true</jk> if the constructor is at least as visible as this object. + */ + public boolean isVisible(Constructor<?> x) { + return isVisible(x.getModifiers()); + } + + /** + * Shortcut for <code>isVisible(x.getModifiers());</code> + * + * @param x The method to check. + * @return <jk>true</jk> if the method is at least as visible as this object. + */ + public boolean isVisible(Method x) { + return isVisible(x.getModifiers()); + } + + /** + * Shortcut for <code>isVisible(x.getModifiers());</code> + * + * @param x The field to check. + * @return <jk>true</jk> if the field is at least as visible as this object. + */ + public boolean isVisible(Field x) { + return isVisible(x.getModifiers()); + } + + /** + * Makes constructor accessible if it matches the visibility requirements, or returns <jk>null</jk> if it doesn't. + * Security exceptions thrown on the call to {@link Constructor#setAccessible(boolean)} are quietly ignored. + * + * @param x The constructor. + * @return The same constructor if visibility requirements met, or <jk>null</jk> if visibility requirement not + * met or call to {@link Constructor#setAccessible(boolean)} throws a security exception. + */ + public <T> Constructor<T> filter(Constructor<T> x) { + if (x == null) + return null; + if (isVisible(x)) + if (! setAccessible(x)) + return null; + return x; + } + + /** + * Makes method accessible if it matches the visibility requirements, or returns <jk>null</jk> if it doesn't. + * Security exceptions thrown on the call to {@link Method#setAccessible(boolean)} are quietly ignored. + * + * @param x The method. + * @return The same method if visibility requirements met, or <jk>null</jk> if visibility requirement not + * met or call to {@link Method#setAccessible(boolean)} throws a security exception. + */ + public <T> Method filter(Method x) { + if (x == null) + return null; + if (isVisible(x)) + if (! setAccessible(x)) + return null; + return x; + } + + /** + * Makes field accessible if it matches the visibility requirements, or returns <jk>null</jk> if it doesn't. + * Security exceptions thrown on the call to {@link Field#setAccessible(boolean)} are quietly ignored. + * + * @param x The field. + * @return The same field if visibility requirements met, or <jk>null</jk> if visibility requirement not + * met or call to {@link Field#setAccessible(boolean)} throws a security exception. + */ + public Field filter(Field x) { + if (x == null) + return null; + if (isVisible(x)) + if (! setAccessible(x)) + return null; + return x; + } + + /** + * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. + * + * @param x The constructor. + * @return <jk>true</jk> if call was successful. + */ + public static boolean setAccessible(Constructor<?> x) { + try { + if (! (x == null || x.isAccessible())) + x.setAccessible(true); + return true; + } catch (SecurityException e) { + return false; + } + } + + /** + * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. + * + * @param x The method. + * @return <jk>true</jk> if call was successful. + */ + public static boolean setAccessible(Method x) { + try { + if (! (x == null || x.isAccessible())) + x.setAccessible(true); + return true; + } catch (SecurityException e) { + return false; + } + } + + /** + * Attempts to call <code>x.setAccessible(<jk>true</jk>)</code> and quietly ignores security exceptions. + * + * @param x The field. + * @return <jk>true</jk> if call was successful. + */ + public static boolean setAccessible(Field x) { + try { + if (! (x == null || x.isAccessible())) + x.setAccessible(true); + return true; + } catch (SecurityException e) { + return false; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.class new file mode 100755 index 0000000..ceb79f5 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.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/Writable.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java new file mode 100755 index 0000000..0c7cbd2 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/Writable.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Licensed Materials - Property of IBM + * (c) Copyright IBM Corporation 2015. All Rights Reserved. + * + * Note to U.S. Government Users Restricted Rights: Use, + * duplication or disclosure restricted by GSA ADP Schedule + * Contract with IBM Corp. + *******************************************************************************/ +package com.ibm.juno.core; + +import java.io.*; + +/** + * Interface that identifies that an object can be serialized directly to a writer. + * <p> + * Instances must identify the media type of the content by implementing the + * {@link #getMediaType()} method. + * </p> + * + * @author James Bognar ([email protected]) + */ +public interface Writable { + + /** + * Serialize this object to the specified writer. + * + * @param w The writer to write to. + * @throws IOException + */ + void writeTo(Writer w) throws IOException; + + /** + * Returns the serialized media type for this resource (e.g. <js>"text/html"</js>) + * + * @return The media type, or <jk>null</jk> if the media type is not known. + */ + String getMediaType(); +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.class new file mode 100755 index 0000000..a9f3acd Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.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/annotation/Bean.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java new file mode 100755 index 0000000..ccdb7f7 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/Bean.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * 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.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.beans.*; +import java.lang.annotation.*; + +import com.ibm.juno.core.*; +import com.ibm.juno.core.filter.*; + +/** + * Used to tailor how beans get interpreted by the framework. + * <p> + * Can be used to do the following: + * <ul> + * <li>Explicitly specify the set and order of properties on a bean. + * <li>Associate a {@link PropertyNamer} with a class. + * <li>Specify subtypes of a bean differentiated by a sub type property. + * </ul> + * <p> + * This annotation can be applied to classes and interfaces. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target(TYPE) +@Retention(RUNTIME) +@Inherited +public @interface Bean { + + /** + * The set and order of names of properties associated with a bean class. + * <p> + * The order specified is the same order that the entries will be returned by the {@link BeanMap#entrySet()} and related methods. + * <p> + * This annotation is an alternative to using the {@link BeanFilter} class with an implemented {@link BeanFilter#getProperties()} method. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Address class with only street/city/state properties (in that order).</jc> + * <jc>// All other properties are ignored.</jc> + * <ja>@Bean</ja>(properties={<js>"street"</js>,<js>"city"</js>,<js>"state"</js>}) + * <jk>public class</jk> Address { + * ... + * </p> + * </dd> + * </dl> + */ + String[] properties() default {}; + + /** + * Specifies a list of properties that should be excluded from {@link BeanMap#entrySet()}. + * <p> + * This annotation is an alternative to using the {@link BeanFilter} class with an implemented {@link BeanFilter#getExcludeProperties()} method. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Address class with only street/city/state properties (in that order).</jc> + * <jc>// All other properties are ignored.</jc> + * <ja>@Bean</ja>(excludeProperties={<js>"city"</js>,<js>"state"</js>}) + * <jk>public class</jk> Address { + * ... + * </p> + * </dd> + * </dl> + */ + String[] excludeProperties() default {}; + + /** + * Associates a {@link PropertyNamer} with this bean to tailor the names of the bean properties. + * <p> + * Property namers are used to transform bean property names from standard form to some other form. + * For example, the {@link PropertyNamerDashedLC} will convert property names to dashed-lowercase, and + * these will be used as attribute names in JSON, and element names in XML. + * <p> + * This annotation is an alternative to using the {@link BeanFilter} class with an implemented {@link BeanFilter#getPropertyNamer()} method. + * + * <dl> + * <dt>Example:</dt> + * <dd> + * <p class='bcode'> + * <jc>// Define a class with dashed-lowercase property names.</jc> + * <ja>@Bean</ja>(propertyNamer=PropertyNamerDashedLC.<jk>class</jk>) + * <jk>public class</jk> MyClass { + * ... + * } + * </p> + * </dd> + * </dl> + */ + Class<? extends PropertyNamer> propertyNamer() default PropertyNamerDefault.class; + + /** + * Defines a virtual property on a superclass that identifies bean subtype classes. + * <p> + * In the following example, the abstract class has two subclasses that are differentiated + * by a property called <code>subType</code> + * <p class='bcode'> + * <jc>// Abstract superclass</jc> + * <ja>@Bean</ja>( + * subTypeProperty=<js>"subType"</js>, + * subTypes={ + * <ja>@BeanSubType</ja>(type=A1.<jk>class</jk>, id=<js>"A1"</js>), + * <ja>@BeanSubType</ja>(type=A2.<jk>class</jk>, id=<js>"A2"</js>) + * } + * ) + * <jk>public class</jk> A { + * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; + * } + * + * <jc>// Subclass 1</jc> + * <jk>public class</jk> A1 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f1</jf>; + * } + * + * <jc>// Subclass 2</jc> + * <jk>public class</jk> A2 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f2</jf>; + * } + * </p> + * <p> + * The following shows what happens when serializing a subclassed object to JSON: + * <p> + * <p class='bcode'> + * JsonSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>; + * A1 a1 = <jk>new</jk> A1(); + * a1.<jf>f1</jf> = <js>"f1"</js>; + * String r = s.serialize(a1); + * <jsm>assertEquals</jsm>(<js>"{subType:'A1',f1:'f1',f0:'f0'}"</js>, r); + * </p> + * <p> + * The following shows what happens when parsing back into the original object. + * <p> + * <p class='bcode'> + * JsonParser p = JsonParser.<jsf>DEFAULT</jsf>; + * A a = p.parse(r, A.<jk>class</jk>); + * <jsm>assertTrue</jsm>(a <jk>instanceof</jk> A1); + * </p> + * <p> + * This annotation is an alternative to using the {@link BeanFilter} class with an implemented {@link BeanFilter#getSubTypeProperty()} method. + */ + String subTypeProperty() default ""; + + /** + * Used in conjunction with {@link #subTypeProperty()} to set up bean subtypes. + */ + BeanSubType[] subTypes() default {}; + + /** + * Identifies a class to be used as the interface class for this and all subclasses. + * <p> + * When specified, only the list of properties defined on the interface class will be used during serialization. + * Additional properties on subclasses will be ignored. + * <p class='bcode'> + * <jc>// Parent class</jc> + * <ja>@Bean</ja>(interfaceClass=A.<jk>class</jk>) + * <jk>public abstract class</jk> A { + * <jk>public</jk> String <jf>f0</jf> = <js>"f0"</js>; + * } + * + * <jc>// Sub class</jc> + * <jk>public class</jk> A1 <jk>extends</jk> A { + * <jk>public</jk> String <jf>f1</jf> = <js>"f1"</js>; + * } + * + * JsonSerializer s = JsonSerializer.<jsf>DEFAULT_LAX</jsf>; + * A1 a1 = <jk>new</jk> A1(); + * String r = s.serialize(a1); + * <jsm>assertEquals</jsm>(<js>"{f0:'f0'}"</js>, r); // Note f1 is not serialized. + * </p> + * <p> + * Note that this annotation can be used on the parent class so that it filters to all child classes, + * or can be set individually on the child classes. + * <p> + * This annotation is an alternative to using the {@link BeanFilter} class with an implemented {@link BeanFilter#getInterfaceClass()} method. + */ + Class<?> interfaceClass() default Object.class; + + /** + * Identifies a stop class for the annotated class. + * <p> + * Identical in purpose to the stop class specified by {@link Introspector#getBeanInfo(Class, Class)}. + * Any properties in the stop class or in its baseclasses will be ignored during analysis. + * <p> + * For example, in the following class hierarchy, instances of <code>C3</code> will include property <code>p3</code>, but + * not <code>p1</code> or <code>p2</code>. + * <p class='bcode'> + * <jk>public class</jk> C1 { + * <jk>public int</jk> getP1(); + * } + * + * <jk>public class</jk> C2 <jk>extends</jk> C1 { + * <jk>public int</jk> getP2(); + * } + * + * <ja>@Bean</ja>(stopClass=C2.<jk>class</jk>) + * <jk>public class</jk> C3 <jk>extends</jk> C2 { + * <jk>public int</jk> getP3(); + * } + * </p> + */ + Class<?> stopClass() default Object.class; +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.class new file mode 100755 index 0000000..3a5d88f Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.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/annotation/BeanConstructor.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java new file mode 100755 index 0000000..b9153e9 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanConstructor.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * 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.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +import com.ibm.juno.core.*; + +/** + * Maps constructor arguments to property names on beans with read-only properties. + * <p> + * This annotation can be used in the case of beans with properties whose values can only be set by passing + * them in through a constructor on the class.<br> + * Since method parameter names are lost during compilation, this annotation essentially redefines them + * so that they are available at runtime. + * <p> + * The definition of a read-only bean is a bean with properties with only getters, like shown below... + * <p class='bcode'> + * <jk>public class</jk> Person { + * <jk>private final</jk> String <jf>name</jf>; + * <jk>private final int</jk> <jf>age</jf>; + * + * <ja>@BeanConstructor</ja>(properties={<js>"name"</js>,<js>"age"</js>}) + * <jk>public</jk> Person(String name, <jk>int</jk> age) { + * <jk>this</jk>.<jf>name</jf> = name; + * <jk>this</jk>.<jf>age</jf> = age; + * } + * + * <jc>// Read only properties.</jc> + * + * <jk>public</jk> String getName() { + * <jk>return</jk> <jf>name</jf>; + * } + * + * <jk>public int</jk> getAge() { + * <jk>return</jk> <jf>age</jf>; + * } + * } + * + * String json = <js>"{name:'John Smith',age:45}"</js>; + * Person p = JsonParser.<jsf>DEFAULT</jsf>.parse(json); + * String name = p.getName(); <jc>// "John Smith"</jc> + * <jk>int</jk> age = p.getAge(); <jc>// 45</jc> + * </p> + * <p> + * This annotation can only be applied to constructors and can only be applied to one constructor per class. + * <p> + * When present, bean instantiation is delayed until the call to {@link BeanMap#getBean()}. + * Until then, bean property values are stored in a local cache until <code>getBean()</code> is called. + * Because of this additional caching step, parsing into read-only beans tends to be slower and use + * more memory than parsing into beans with writable properties. + * <p> + * Attempting to call {@link BeanMap#put(String,Object)} on a read-only property after calling {@link BeanMap#getBean()} + * will result in a {@link BeanRuntimeException} being thrown. + * Multiple calls to {@link BeanMap#getBean()} will return the same bean instance. + * <p> + * Beans can be defined with a combination of read-only and read-write properties. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target(CONSTRUCTOR) +@Retention(RUNTIME) +@Inherited +public @interface BeanConstructor { + + /** + * The names of the properties of the constructor arguments. + * <p> + * The number of properties listed must match the number of arguments in the constructor. + */ + String[] properties() default {}; +} http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.class new file mode 100755 index 0000000..4e1f137 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.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/annotation/BeanIgnore.java ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java new file mode 100755 index 0000000..b71b3f8 --- /dev/null +++ b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanIgnore.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * 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.annotation; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.*; + +import java.lang.annotation.*; + +/** + * Ignore classes, fields, and methods from being interpreted as bean or bean components. + * <p> + * Applied to classes that may look like beans, but you want to be treated as non-beans. + * For example, if you want to force a bean to be converted to a string using the <code>toString()</code> + * method, use this annoation on the class. + * <p> + * Applies to fields that should not be interpreted as bean property fields. + * <p> + * Applies to getters or setters that should not be interpreted as bean property getters or setters. + * + * @author James Bognar ([email protected]) + */ +@Documented +@Target({FIELD,METHOD,TYPE}) +@Retention(RUNTIME) +@Inherited +public @interface BeanIgnore {} + http://git-wip-us.apache.org/repos/asf/incubator-juneau/blob/7e4f63e6/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class ---------------------------------------------------------------------- diff --git a/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class new file mode 100755 index 0000000..49c9274 Binary files /dev/null and b/com.ibm.team.juno.releng/bin/core/com/ibm/juno/core/annotation/BeanProperty.class differ
