org.apache.juneau.BeanSession Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * 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 static org.apache.juneau.internal.ClassUtils.*;
import static org.apache.juneau.internal.ThrowableUtils.*;
import static org.apache.juneau.internal.StringUtils.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.atomic.*;
import org.apache.juneau.http.*;
import org.apache.juneau.internal.*;
import org.apache.juneau.json.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.serializer.*;
import org.apache.juneau.transform.*;
/**
* Session object that lives for the duration of a single use of {@link Serializer} or {@link Parser}.
*
*
* This class is NOT thread safe. It is meant to be discarded after one-time use.
*/
@SuppressWarnings({"unchecked","rawtypes"})
public class BeanSession extends Session {
private final BeanContext ctx;
private final Locale locale;
private final TimeZone timeZone;
private final MediaType mediaType;
private final boolean debug;
private Stack sbStack = new Stack();
/**
* Create a new session using properties specified in the context.
*
* @param ctx
* The context creating this session object.
* The context contains all the configuration settings for this object.
* @param args
* Runtime session arguments.
*/
protected BeanSession(BeanContext ctx, BeanSessionArgs args) {
super(ctx, args);
this.ctx = ctx;
Locale _locale = null;
ObjectMap p = getProperties();
if (p == null) {
_locale = (args.locale != null ? args.locale : ctx.locale);
this.timeZone = (args.timeZone != null ? args.timeZone : ctx.timeZone);
this.debug = ctx.debug;
this.mediaType = args.mediaType != null ? args.mediaType : ctx.mediaType;
} else {
_locale = (args.locale != null ? args.locale : getPropertyWithDefault(BEAN_locale, ctx.locale, Locale.class));
this.timeZone = (args.timeZone != null ? args.timeZone : getPropertyWithDefault(BEAN_timeZone, ctx.timeZone, TimeZone.class));
this.debug = getPropertyWithDefault(BEAN_debug, false, boolean.class);
this.mediaType = (args.mediaType != null ? args.mediaType : getPropertyWithDefault(BEAN_mediaType, ctx.mediaType, MediaType.class));
}
this.locale = _locale == null ? Locale.getDefault() : _locale;
}
@Override /* Session */
public ObjectMap asMap() {
return super.asMap()
.appendAll(ctx.asMap())
.append("BeanSession", new ObjectMap()
.append("debug", debug)
.append("locale", locale)
.append("mediaType", mediaType)
.append("timeZone", timeZone)
);
}
/**
* Returns the locale defined on this session.
*
*
* The locale is determined in the following order:
*
* locale
parameter passed in through constructor.
* - {@link BeanContext#BEAN_locale} entry in
properties
parameter passed in through constructor.
* - {@link BeanContext#BEAN_locale} setting on bean context.
*
- Locale returned by {@link Locale#getDefault()}.
*
*
* @return The session locale.
*/
public final Locale getLocale() {
return locale;
}
/**
* Returns the timezone defined on this session.
*
*
* The timezone is determined in the following order:
*
* timeZone
parameter passed in through constructor.
* - {@link BeanContext#BEAN_timeZone} entry in
properties
parameter passed in through constructor.
* - {@link BeanContext#BEAN_timeZone} setting on bean context.
*
*
* @return The session timezone, or null if timezone not specified.
*/
public final TimeZone getTimeZone() {
return timeZone;
}
/**
* Returns the {@link BeanContext#BEAN_debug} setting value for this session.
*
* @return The {@link BeanContext#BEAN_debug} setting value for this session.
*/
public final boolean isDebug() {
return debug;
}
/**
* Bean property getter: ignoreUnknownBeanProperties .
*
*
* See {@link BeanContext#BEAN_ignoreUnknownBeanProperties}.
*
* @return The value of the ignoreUnknownBeanProperties property on this bean.
*/
public final boolean isIgnoreUnknownBeanProperties() {
return ctx.ignoreUnknownBeanProperties;
}
/**
* Converts the specified value to the specified class type.
*
*
* See {@link #convertToType(Object, ClassMeta)} for the list of valid conversions.
*
* @param The class type to convert the value to.
* @param value The value to convert.
* @param type The class type to convert the value to.
* @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
* @return The converted value.
*/
public final T convertToType(Object value, Class type) throws InvalidDataConversionException {
// Shortcut for most common case.
if (value != null && value.getClass() == type)
return (T)value;
return convertToMemberType(null, value, ctx.getClassMeta(type));
}
/**
* Same as {@link #convertToType(Object, Class)}, except used for instantiating inner member classes that must
* be instantiated within another class instance.
*
* @param The class type to convert the value to.
* @param outer
* If class is a member class, this is the instance of the containing class.
* Should be null if not a member class.
* @param value The value to convert.
* @param type The class type to convert the value to.
* @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
* @return The converted value.
*/
public final T convertToMemberType(Object outer, Object value, Class type) throws InvalidDataConversionException {
return convertToMemberType(outer, value, ctx.getClassMeta(type));
}
/**
* Casts the specified value into the specified type.
*
*
* If the value isn't an instance of the specified type, then converts the value if possible.
*
*
* The following conversions are valid:
*
* Convert to type Valid input value types Notes
*
*
* A class that is the normal type of a registered {@link PojoSwap}.
*
*
* A value whose class matches the transformed type of that registered {@link PojoSwap}.
*
*
*
*
*
* A class that is the transformed type of a registered {@link PojoSwap}.
*
*
* A value whose class matches the normal type of that registered {@link PojoSwap}.
*
*
*
*
*
* {@code Number} (e.g. {@code Integer}, {@code Short}, {@code Float},...)
*
Number.TYPE
(e.g. Integer.TYPE
,
* Short.TYPE
, Float.TYPE
,...)
*
*
* {@code Number}, {@code String}, null
*
*
* For primitive {@code TYPES}, null returns the JVM default value for that type.
*
*
*
*
* {@code Map} (e.g. {@code Map}, {@code HashMap}, {@code TreeMap}, {@code ObjectMap})
*
*
* {@code Map}
*
*
* If {@code Map} is not constructible, a {@code ObjectMap} is created.
*
*
*
*
* {@code Collection} (e.g. {@code List}, {@code LinkedList}, {@code HashSet}, {@code ObjectList})
*
*
* {@code Collection
*
* If {@code Collection} is not constructible, a {@code ObjectList} is created.
*
*
*
*
* {@code X[]} (array of any type X)
*
*
* {@code List}
*
*
*
*
*
* {@code X[][]} (multi-dimensional arrays)
*
*
* {@code List>}
*
{@code List}
*
{@code List[]}
*
*
*
*
*
* {@code Enum}
*
*
* {@code String}
*
*
*
*
*
* Bean
*
*
* {@code Map}
*
*
*
*
*
* {@code String}
*
*
* Anything
*
*
* Arrays are converted to JSON arrays
*
*
*
*
* Anything with one of the following methods:
*
public static T fromString(String)
*
public static T valueOf(String)
*
public T(String)
*
*
* String
*
*
*
*
*
*
*
* @param The class type to convert the value to.
* @param value The value to be converted.
* @param type The target object type.
* @return The converted type.
* @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
*/
public final T convertToType(Object value, ClassMeta type) throws InvalidDataConversionException {
return convertToMemberType(null, value, type);
}
/**
* Same as {@link #convertToType(Object, Class)}, but allows for complex data types consisting of collections or maps.
*
* @param The class type to convert the value to.
* @param value The value to be converted.
* @param type The target object type.
* @param args The target object parameter types.
* @return The converted type.
* @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
*/
public final T convertToType(Object value, Type type, Type...args) throws InvalidDataConversionException {
return (T)convertToMemberType(null, value, getClassMeta(type, args));
}
/**
* Same as {@link #convertToType(Object, ClassMeta)}, except used for instantiating inner member classes that must
* be instantiated within another class instance.
*
* @param The class type to convert the value to.
* @param outer
* If class is a member class, this is the instance of the containing class.
* Should be null if not a member class.
* @param value The value to convert.
* @param type The class type to convert the value to.
* @throws InvalidDataConversionException If the specified value cannot be converted to the specified type.
* @return The converted value.
*/
public final T convertToMemberType(Object outer, Object value, ClassMeta type) throws InvalidDataConversionException {
if (type == null)
type = (ClassMeta)ctx.object();
try {
// Handle the case of a null value.
if (value == null) {
// If it's a primitive, then use the converters to get the default value for the primitive type.
if (type.isPrimitive())
return type.getPrimitiveDefault();
// Otherwise, just return null.
return null;
}
Class tc = type.getInnerClass();
// If no conversion needed, then just return the value.
// Don't include maps or collections, because child elements may need conversion.
if (tc.isInstance(value))
if (! ((type.isMap() && type.getValueType().isNotObject()) || (type.isCollection() && type.getElementType().isNotObject())))
return (T)value;
if (tc == Class.class)
return (T)(ctx.classLoader.loadClass(value.toString()));
PojoSwap swap = type.getPojoSwap(this);
if (swap != null) {
Class> nc = swap.getNormalClass(), fc = swap.getSwapClass();
if (isParentClass(nc, tc) && isParentClass(fc, value.getClass()))
return (T)swap.unswap(this, value, type);
}
ClassMeta> vt = ctx.getClassMetaForObject(value);
swap = vt.getPojoSwap(this);
if (swap != null) {
Class> nc = swap.getNormalClass(), fc = swap.getSwapClass();
if (isParentClass(nc, vt.getInnerClass()) && isParentClass(fc, tc))
return (T)swap.swap(this, value);
}
if (type.isPrimitive()) {
if (value.toString().isEmpty())
return type.getPrimitiveDefault();
if (type.isNumber()) {
if (value instanceof Number) {
Number n = (Number)value;
if (tc == Integer.TYPE)
return (T)Integer.valueOf(n.intValue());
if (tc == Short.TYPE)
return (T)Short.valueOf(n.shortValue());
if (tc == Long.TYPE)
return (T)Long.valueOf(n.longValue());
if (tc == Float.TYPE)
return (T)Float.valueOf(n.floatValue());
if (tc == Double.TYPE)
return (T)Double.valueOf(n.doubleValue());
if (tc == Byte.TYPE)
return (T)Byte.valueOf(n.byteValue());
} else {
String n = null;
if (value instanceof Boolean)
n = ((Boolean)value).booleanValue() ? "1" : "0";
else
n = value.toString();
int multiplier = (tc == Integer.TYPE || tc == Short.TYPE || tc == Long.TYPE) ? getMultiplier(n) : 1;
if (multiplier != 1) {
n = n.substring(0, n.length()-1).trim();
Long l = Long.valueOf(n) * multiplier;
if (tc == Integer.TYPE)
return (T)Integer.valueOf(l.intValue());
if (tc == Short.TYPE)
return (T)Short.valueOf(l.shortValue());
if (tc == Long.TYPE)
return (T)Long.valueOf(l.longValue());
} else {
if (tc == Integer.TYPE)
return (T)Integer.valueOf(n);
if (tc == Short.TYPE)
return (T)Short.valueOf(n);
if (tc == Long.TYPE)
return (T)Long.valueOf(n);
if (tc == Float.TYPE)
return (T)new Float(n);
if (tc == Double.TYPE)
return (T)new Double(n);
if (tc == Byte.TYPE)
return (T)Byte.valueOf(n);
}
}
} else if (type.isChar()) {
String s = value.toString();
return (T)Character.valueOf(s.length() == 0 ? 0 : s.charAt(0));
} else if (type.isBoolean()) {
if (value instanceof Number) {
int i = ((Number)value).intValue();
return (T)(i == 0 ? Boolean.FALSE : Boolean.TRUE);
}
return (T)Boolean.valueOf(value.toString());
}
}
if (type.isNumber()) {
if (value instanceof Number) {
Number n = (Number)value;
if (tc == Integer.class)
return (T)Integer.valueOf(n.intValue());
if (tc == Short.class)
return (T)Short.valueOf(n.shortValue());
if (tc == Long.class)
return (T)Long.valueOf(n.longValue());
if (tc == Float.class)
return (T)Float.valueOf(n.floatValue());
if (tc == Double.class)
return (T)Double.valueOf(n.doubleValue());
if (tc == Byte.class)
return (T)Byte.valueOf(n.byteValue());
if (tc == Byte.class)
return (T)Byte.valueOf(n.byteValue());
if (tc == AtomicInteger.class)
return (T)new AtomicInteger(n.intValue());
if (tc == AtomicLong.class)
return (T)new AtomicLong(n.intValue());
} else {
if (value.toString().isEmpty())
return null;
String n = null;
if (value instanceof Boolean)
n = ((Boolean)value).booleanValue() ? "1" : "0";
else
n = value.toString();
int multiplier = (tc == Integer.class || tc == Short.class || tc == Long.class) ? getMultiplier(n) : 1;
if (multiplier != 1) {
n = n.substring(0, n.length()-1).trim();
Long l = Long.valueOf(n) * multiplier;
if (tc == Integer.TYPE)
return (T)Integer.valueOf(l.intValue());
if (tc == Short.TYPE)
return (T)Short.valueOf(l.shortValue());
if (tc == Long.TYPE)
return (T)Long.valueOf(l.longValue());
} else {
if (tc == Integer.class)
return (T)Integer.valueOf(n);
if (tc == Short.class)
return (T)Short.valueOf(n);
if (tc == Long.class)
return (T)Long.valueOf(n);
if (tc == Float.class)
return (T)new Float(n);
if (tc == Double.class)
return (T)new Double(n);
if (tc == Byte.class)
return (T)Byte.valueOf(n);
if (tc == AtomicInteger.class)
return (T)new AtomicInteger(Integer.valueOf(n));
if (tc == AtomicLong.class)
return (T)new AtomicLong(Long.valueOf(n));
}
}
}
if (type.isChar()) {
String s = value.toString();
return (T)Character.valueOf(s.length() == 0 ? 0 : s.charAt(0));
}
// Handle setting of array properties
if (type.isArray()) {
if (vt.isCollection())
return (T)toArray(type, (Collection)value);
else if (vt.isArray())
return (T)toArray(type, Arrays.asList((Object[])value));
else if (startsWith(value.toString(), '['))
return (T)toArray(type, new ObjectList(value.toString()).setBeanSession(this));
else
return (T)toArray(type, new ObjectList((Object[])StringUtils.split(value.toString())).setBeanSession(this));
}
// Target type is some sort of Map that needs to be converted.
if (type.isMap()) {
try {
if (value instanceof Map) {
Map m = type.canCreateNewInstance(outer) ? (Map)type.newInstance(outer) : new ObjectMap(this);
ClassMeta keyType = type.getKeyType(), valueType = type.getValueType();
for (Map.Entry e : (Set)((Map)value).entrySet()) {
Object k = e.getKey();
if (keyType.isNotObject()) {
if (keyType.isString() && k.getClass() != Class.class)
k = k.toString();
else
k = convertToMemberType(m, k, keyType);
}
Object v = e.getValue();
if (valueType.isNotObject())
v = convertToMemberType(m, v, valueType);
m.put(k, v);
}
return (T)m;
} else if (!type.canCreateNewInstanceFromString(outer)) {
ObjectMap m = new ObjectMap(value.toString(), ctx.defaultParser);
return convertToMemberType(outer, m, type);
}
} catch (Exception e) {
throw new InvalidDataConversionException(value.getClass(), type, e);
}
}
// Target type is some sort of Collection
if (type.isCollection()) {
try {
Collection l = type.canCreateNewInstance(outer) ? (Collection)type.newInstance(outer) : new ObjectList(this);
ClassMeta elementType = type.getElementType();
if (value.getClass().isArray())
for (Object o : (Object[])value)
l.add(elementType.isObject() ? o : convertToMemberType(l, o, elementType));
else if (value instanceof Collection)
for (Object o : (Collection)value)
l.add(elementType.isObject() ? o : convertToMemberType(l, o, elementType));
else if (value instanceof Map)
l.add(elementType.isObject() ? value : convertToMemberType(l, value, elementType));
else if (! value.toString().isEmpty())
throw new InvalidDataConversionException(value.getClass(), type, null);
return (T)l;
} catch (InvalidDataConversionException e) {
throw e;
} catch (Exception e) {
throw new InvalidDataConversionException(value.getClass(), type, e);
}
}
if (type.isEnum()) {
if (type.canCreateNewInstanceFromString(outer))
return type.newInstanceFromString(outer, value.toString());
return (T)Enum.valueOf((Class extends Enum>)tc, value.toString());
}
if (type.isString()) {
if (vt.isMapOrBean() || vt.isCollectionOrArray()) {
if (JsonSerializer.DEFAULT_LAX != null)
return (T)JsonSerializer.DEFAULT_LAX.serialize(value);
} else if (vt.isClass()) {
return (T)getReadableClassName((Class>)value);
}
return (T)value.toString();
}
if (type.isCharSequence()) {
Class> c = value.getClass();
if (c.isArray()) {
if (c.getComponentType().isPrimitive()) {
ObjectList l = new ObjectList(this);
int size = Array.getLength(value);
for (int i = 0; i < size; i++)
l.add(Array.get(value, i));
value = l;
}
value = new ObjectList((Object[])value).setBeanSession(this);
}
return type.newInstanceFromString(outer, value.toString());
}
if (type.isBoolean()) {
if (value instanceof Number)
return (T)(Boolean.valueOf(((Number)value).intValue() != 0));
return (T)Boolean.valueOf(value.toString());
}
// It's a bean being initialized with a Map
if (type.isBean() && value instanceof Map) {
if (value instanceof ObjectMap) {
ObjectMap m2 = (ObjectMap)value;
String typeName = m2.getString(getBeanTypePropertyName(type));
if (typeName != null) {
ClassMeta cm = type.getBeanRegistry().getClassMeta(typeName);
if (cm != null && isParentClass(type.innerClass, cm.innerClass))
return (T)m2.cast(cm);
}
}
return newBeanMap(tc).load((Map,?>) value).getBean();
}
if (type.canCreateNewInstanceFromNumber(outer) && value instanceof Number)
return type.newInstanceFromNumber(this, outer, (Number)value);
if (type.canCreateNewInstanceFromString(outer))
return type.newInstanceFromString(outer, value.toString());
if (type.isBean())
return newBeanMap(type.getInnerClass()).load(value.toString()).getBean();
} catch (Exception e) {
throw new InvalidDataConversionException(value, type, e);
}
throw new InvalidDataConversionException(value, type, null);
}
private static int getMultiplier(String s) {
if (s.endsWith("G"))
return 1024*1024*1024;
if (s.endsWith("M"))
return 1024*1024;
if (s.endsWith("K"))
return 1024;
return 1;
}
/**
* Converts the contents of the specified list into an array.
*
*
* Works on both object and primitive arrays.
*
*
* In the case of multi-dimensional arrays, the incoming list must contain elements of type n-1 dimension.
* i.e. if {@code type} is int [][]
then {@code list} must have entries of type
* int []
.
*
* @param type The type to convert to. Must be an array type.
* @param list The contents to populate the array with.
* @return A new object or primitive array.
*/
public final Object toArray(ClassMeta> type, Collection> list) {
if (list == null)
return null;
ClassMeta> componentType = type.isArgs() ? object() : type.getElementType();
Object array = Array.newInstance(componentType.getInnerClass(), list.size());
int i = 0;
for (Object o : list) {
if (! type.getInnerClass().isInstance(o)) {
if (componentType.isArray() && o instanceof Collection)
o = toArray(componentType, (Collection>)o);
else if (o == null && componentType.isPrimitive())
o = componentType.getPrimitiveDefault();
else
o = convertToType(o, componentType);
}
try {
Array.set(array, i++, o);
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw e;
}
}
return array;
}
/**
* Wraps an object inside a {@link BeanMap} object (i.e. a modifiable {@link Map}).
*
*
* If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a
* bean.
*
*
Example:
*
* // Construct a bean map around a bean instance
* BeanMap<Person> bm = BeanContext.DEFAULT .forBean(new Person());
*
*
* @param The class of the object being wrapped.
* @param o The object to wrap in a map interface. Must not be null.
* @return The wrapped object.
*/
public final BeanMap toBeanMap(T o) {
return this.toBeanMap(o, (Class)o.getClass());
}
/**
* Determines whether the specified object matches the requirements on this context of being a bean.
*
* @param o The object being tested.
* @return true if the specified object is considered a bean.
*/
public final boolean isBean(Object o) {
if (o == null)
return false;
return isBean(o.getClass());
}
/**
* Determines whether the specified class matches the requirements on this context of being a bean.
*
* @param c The class being tested.
* @return true if the specified class is considered a bean.
*/
public final boolean isBean(Class> c) {
return getBeanMeta(c) != null;
}
/**
* Wraps an object inside a {@link BeanMap} object (i.e.: a modifiable {@link Map}) defined as a bean for one of its
* class, a super class, or an implemented interface.
*
*
* If object is not a true bean, throws a {@link BeanRuntimeException} with an explanation of why it's not a bean.
*
*
Example:
*
* // Construct a bean map for new bean using only properties defined in a superclass
* BeanMap<MySubBean> bm = BeanContext.DEFAULT .forBean(new MySubBean(), MySuperBean.class );
*
* // Construct a bean map for new bean using only properties defined in an interface
* BeanMap<MySubBean> bm = BeanContext.DEFAULT .forBean(new MySubBean(), MySuperInterface.class );
*
*
* @param The class of the object being wrapped.
* @param o The object to wrap in a bean interface. Must not be null.
* @param c The superclass to narrow the bean properties to. Must not be null.
* @return The bean representation, or null if the object is not a true bean.
* @throws NullPointerException If either parameter is null.
* @throws IllegalArgumentException If the specified object is not an an instance of the specified class.
* @throws
* BeanRuntimeException If specified object is not a bean according to the bean rules specified in this context
* class.
*/
public final BeanMap toBeanMap(T o, Class super T> c) throws BeanRuntimeException {
assertFieldNotNull(o, "o");
assertFieldNotNull(c, "c");
if (! c.isInstance(o))
illegalArg("The specified object is not an instance of the specified class. class=''{0}'', objectClass=''{1}'', object=''{2}''", c.getName(), o.getClass().getName(), 0);
ClassMeta cm = getClassMeta(c);
BeanMeta m = cm.getBeanMeta();
if (m == null)
throw new BeanRuntimeException(c, "Class is not a bean. Reason=''{0}''", cm.getNotABeanReason());
return new BeanMap(this, o, m);
}
/**
* Creates a new {@link BeanMap} object (i.e. a modifiable {@link Map}) of the given class with uninitialized
* property values.
*
*
* If object is not a true bean, then throws a {@link BeanRuntimeException} with an explanation of why it's not a
* bean.
*
*
Example:
*
* // Construct a new bean map wrapped around a new Person object
* BeanMap<Person> bm = BeanContext.DEFAULT .newBeanMap(Person.class );
*
*
* @param The class of the object being wrapped.
* @param c The name of the class to create a new instance of.
* @return A new instance of the class.
*/
public final BeanMap newBeanMap(Class c) {
return newBeanMap(null, c);
}
/**
* Same as {@link #newBeanMap(Class)}, except used for instantiating inner member classes that must be instantiated
* within another class instance.
*
* @param The class of the object being wrapped.
* @param c The name of the class to create a new instance of.
* @param outer
* If class is a member class, this is the instance of the containing class.
* Should be null if not a member class.
* @return A new instance of the class.
*/
public final BeanMap newBeanMap(Object outer, Class c) {
BeanMeta m = getBeanMeta(c);
if (m == null)
return null;
T bean = null;
if (m.constructorArgs.length == 0)
bean = newBean(outer, c);
return new BeanMap(this, bean, m);
}
/**
* Creates a new empty bean of the specified type, except used for instantiating inner member classes that must
* be instantiated within another class instance.
*
* Example:
*
* // Construct a new instance of the specified bean class
* Person p = BeanContext.DEFAULT .newBean(Person.class );
*
*
* @param The class type of the bean being created.
* @param c The class type of the bean being created.
* @return A new bean object.
* @throws BeanRuntimeException If the specified class is not a valid bean.
*/
public final T newBean(Class c) throws BeanRuntimeException {
return newBean(null, c);
}
/**
* Same as {@link #newBean(Class)}, except used for instantiating inner member classes that must be instantiated
* within another class instance.
*
* @param The class type of the bean being created.
* @param c The class type of the bean being created.
* @param outer
* If class is a member class, this is the instance of the containing class.
* Should be null if not a member class.
* @return A new bean object.
* @throws BeanRuntimeException If the specified class is not a valid bean.
*/
public final T newBean(Object outer, Class c) throws BeanRuntimeException {
ClassMeta cm = getClassMeta(c);
BeanMeta m = cm.getBeanMeta();
if (m == null)
return null;
try {
T o = (T)m.newBean(outer);
if (o == null)
throw new BeanRuntimeException(c, "Class does not have a no-arg constructor.");
return o;
} catch (BeanRuntimeException e) {
throw e;
} catch (Exception e) {
throw new BeanRuntimeException(e);
}
}
/**
* Returns the {@link BeanMeta} class for the specified class.
*
* @param The class type to get the meta-data on.
* @param c The class to get the meta-data on.
* @return
* The {@link BeanMeta} for the specified class, or null if the class
* is not a bean per the settings on this context.
*/
public final BeanMeta getBeanMeta(Class c) {
if (c == null)
return null;
return getClassMeta(c).getBeanMeta();
}
/**
* Returns a {@code ClassMeta} wrapper around a {@link Class} object.
*
* @param The class type being wrapped.
* @param c The class being wrapped.
* @return The class meta object containing information about the class.
*/
public final ClassMeta getClassMeta(Class c) {
return ctx.getClassMeta(c);
}
/**
* Used to resolve ClassMetas
of type Collection
and Map
that have
* ClassMeta
values that themselves could be collections or maps.
*
*
* Collection
meta objects are assumed to be followed by zero or one meta objects indicating the
* element type.
*
*
* Map
meta objects are assumed to be followed by zero or two meta objects indicating the key and value
* types.
*
*
* The array can be arbitrarily long to indicate arbitrarily complex data structures.
*
*
Examples:
*
* getClassMeta(String.class );
- A normal type.
* getClassMeta(List.class );
- A list containing objects.
* getClassMeta(List.class , String.class );
- A list containing strings.
* getClassMeta(LinkedList.class , String.class );
- A linked-list containing
* strings.
* getClassMeta(LinkedList.class , LinkedList.class , String.class );
-
* A linked-list containing linked-lists of strings.
* getClassMeta(Map.class );
- A map containing object keys/values.
* getClassMeta(Map.class , String.class , String.class );
- A map
* containing string keys/values.
* getClassMeta(Map.class , String.class , List.class , MyBean.class );
-
* A map containing string keys and values of lists containing beans.
*
*
* @param type
* The class to resolve.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
* {@link GenericArrayType}
* @param args
* The type arguments of the class if it's a collection or map.
*
Can be any of the following: {@link ClassMeta}, {@link Class}, {@link ParameterizedType},
* {@link GenericArrayType}
*
Ignored if the main type is not a map or collection.
* @return The class meta.
*/
public final ClassMeta getClassMeta(Type type, Type...args) {
return ctx.getClassMeta(type, args);
}
/**
* Given an array of {@link Type} objects, returns a {@link ClassMeta} representing those arguments.
*
*
* Constructs a new meta on each call.
*
* @param classes The array of classes to get class metas for.
* @return The args {@link ClassMeta} object corresponding to the classes. Never null .
*/
public final ClassMeta