/*
* JBoss, Home of Professional Open Source
* Copyright 2007, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.metatype.plugins.types;
import java.lang.ref.WeakReference;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.WeakHashMap;
import javax.management.ObjectName;
import org.jboss.beans.info.spi.BeanInfo;
import org.jboss.beans.info.spi.PropertyInfo;
import org.jboss.config.plugins.property.PropertyConfiguration;
import org.jboss.config.spi.Configuration;
import org.jboss.metatype.api.annotations.CompositeKey;
import org.jboss.metatype.api.annotations.CompositeValue;
import org.jboss.metatype.api.annotations.Generic;
import org.jboss.metatype.api.annotations.MetaTypeConstants;
import org.jboss.metatype.api.types.ArrayMetaType;
import org.jboss.metatype.api.types.CollectionMetaType;
import org.jboss.metatype.api.types.CompositeMetaType;
import org.jboss.metatype.api.types.EnumMetaType;
import org.jboss.metatype.api.types.GenericMetaType;
import org.jboss.metatype.api.types.ImmutableCompositeMetaType;
import org.jboss.metatype.api.types.ImmutableTableMetaType;
import org.jboss.metatype.api.types.MapCompositeMetaType;
import org.jboss.metatype.api.types.MetaType;
import org.jboss.metatype.api.types.MetaTypeFactory;
import org.jboss.metatype.api.types.SimpleMetaType;
import org.jboss.metatype.api.types.TableMetaType;
import org.jboss.metatype.spi.types.MetaTypeBuilder;
import org.jboss.metatype.spi.values.MetaMapper;
import org.jboss.reflect.spi.ArrayInfo;
import org.jboss.reflect.spi.ClassInfo;
import org.jboss.reflect.spi.EnumConstantInfo;
import org.jboss.reflect.spi.EnumInfo;
import org.jboss.reflect.spi.InterfaceInfo;
import org.jboss.reflect.spi.TypeInfo;
/**
* DefaultMetaTypeFactory.
*
* @author <a href="adrian@jboss.com">Adrian Brock</a>
* @author Scott.Stark@jboss.org
* @version $Revision: 85720 $
*/
public class DefaultMetaTypeFactory extends MetaTypeFactory
{
/** The map key */
public static final String MAP_KEY = "key";
/** The map value */
public static final String MAP_VALUE = "value";
/** Map index names */
public static final String[] MAP_INDEX_NAMES = { MAP_KEY };
/** Map item names */
public static final String[] MAP_ITEM_NAMES = { MAP_KEY, MAP_VALUE };
/** The configuration */
private static Configuration configuration;
static
{
configuration = AccessController.doPrivileged(new PrivilegedAction<Configuration>()
{
public Configuration run()
{
return new PropertyConfiguration();
}
});
}
/** The object type info */
private TypeInfo objectTypeInfo = configuration.getTypeInfo(Object.class);
/** The builders */
private Map<Class<?>, WeakReference<MetaTypeBuilder>> builders = new WeakHashMap<Class<?>, WeakReference<MetaTypeBuilder>>();
/**
* Create a new DefaultMetaTypeFactory.
*/
public DefaultMetaTypeFactory()
{
// Special types
setBuilder(Class.class, ClassMetaTypeBuilder.INSTANCE);
setBuilder(ObjectName.class, ObjectNameTypeBuilder.INSTANCE);
}
@Override
public MetaType resolve(Type type)
{
TypeInfo typeInfo = configuration.getTypeInfo(type);
return resolve(typeInfo);
}
/**
* Resolve the meta type
*
* @param typeInfo the type
* @return the meta type
*/
public MetaType resolve(TypeInfo typeInfo)
{
// Look for a cached value
MetaType result = typeInfo.getAttachment(MetaType.class);
if (result == null)
{
// Generate it
result = generate(typeInfo);
// Cache it
typeInfo.setAttachment(MetaType.class.getName(), result);
}
// Return the result
return result;
}
public void setBuilder(Class<?> clazz, MetaTypeBuilder builder)
{
synchronized (builders)
{
if (builder == null)
builders.remove(clazz);
builders.put(clazz, new WeakReference<MetaTypeBuilder>(builder));
}
}
/**
* Generate the metatype
*
* @param typeInfo the type info
* @return the metatype
*/
public MetaType generate(TypeInfo typeInfo)
{
MetaType result = isBuilder(typeInfo);
if (result != null)
return result;
MetaMapper<?> mapper = MetaMapper.getMetaMapper(typeInfo);
if (mapper != null)
return mapper.getMetaType();
result = isGeneric(typeInfo);
if (result != null)
return result;
if (typeInfo.isEnum())
return generateEnum((EnumInfo) typeInfo);
ClassInfo annotationType = isAnnotation(typeInfo);
if (annotationType != null)
return generateAnnotation(annotationType);
if (typeInfo.isArray())
return generateArray((ArrayInfo) typeInfo);
if (typeInfo.isCollection())
return generateCollection((ClassInfo) typeInfo);
if (typeInfo.isMap())
{
// See if this is a Map<String,?> type
ClassInfo classInfo = (ClassInfo) typeInfo;
TypeInfo[] types = classInfo.getActualTypeArguments();
if (types != null)
{
TypeInfo keyType = types[0];
TypeInfo valueType = types[1];
if(keyType.getName().equals(String.class.getName()))
{
// Use MapCompositeMetaType
MetaType valueMetaType = resolve(valueType);
return new MapCompositeMetaType(valueMetaType);
}
}
// Map java.util.Properties to MapCompositeMetaType(SimpleMetaType.STRING)
else if(typeInfo.getName().equals(Properties.class.getName()))
{
return new MapCompositeMetaType(SimpleMetaType.STRING);
}
// No, return the general map type
return generateMap(classInfo);
}
result = SimpleMetaType.isSimpleType(typeInfo.getName());
if (result != null)
return result;
return generateBean((ClassInfo) typeInfo);
}
/**
* Generate an enum metatype
*
* @param typeInfo the type info
* @return the metatype
*/
public EnumMetaType generateEnum(EnumInfo typeInfo)
{
EnumConstantInfo[] constants = typeInfo.getEnumConstants();
List<String> validValues = new ArrayList<String>(constants.length);
for (EnumConstantInfo constant : constants)
validValues.add(constant.getName());
return new EnumMetaType(typeInfo.getName(), validValues);
}
/**
* Whether this type is an annotation
*
* @param typeInfo the type info
* @return the annotation type info
*/
public ClassInfo isAnnotation(TypeInfo typeInfo)
{
if (typeInfo.isAnnotation())
return (ClassInfo) typeInfo;
if (typeInfo instanceof ClassInfo)
{
ClassInfo classInfo = (ClassInfo) typeInfo;
InterfaceInfo[] interfaces = classInfo.getInterfaces();
if (interfaces != null)
{
for (int i = 0; i < interfaces.length; ++i)
{
if (interfaces[i].isAnnotation())
return interfaces[i];
}
}
}
// Doesn't look like an annotation
return null;
}
/**
* Generate an annotation metatype
*
* @param typeInfo the type info
* @return the metatype
*/
public CompositeMetaType generateAnnotation(ClassInfo typeInfo)
{
return generateBean(typeInfo);
}
/**
* Generate an array metatype
*
* @param typeInfo the type info
* @return the metatype
*/
@SuppressWarnings("unchecked")
public ArrayMetaType generateArray(ArrayInfo typeInfo)
{
int dimension = 1;
TypeInfo componentType = typeInfo.getComponentType();
while (componentType.isArray())
{
++dimension;
componentType = ((ArrayInfo) componentType).getComponentType();
}
MetaType componentMetaType = resolve(componentType);
boolean isPrimitive = componentType.isPrimitive();
return new ArrayMetaType(dimension, componentMetaType, isPrimitive);
}
/**
* Generate a collection metatype
*
* @param typeInfo the type info
* @return the metatype
*/
@SuppressWarnings("unchecked")
public CollectionMetaType generateCollection(ClassInfo typeInfo)
{
TypeInfo elementType = objectTypeInfo;
TypeInfo[] types = typeInfo.getActualTypeArguments();
if (types != null)
elementType = types[0];
MetaType elementMetaType = resolve(elementType);
return new CollectionMetaType(typeInfo.getName(), elementMetaType);
}
/**
* Generate a map metatype
*
* @param typeInfo the type info
* @return the metatype
*/
public TableMetaType generateMap(ClassInfo typeInfo)
{
TypeInfo keyType = objectTypeInfo;
TypeInfo valueType = objectTypeInfo;
TypeInfo[] types = typeInfo.getActualTypeArguments();
if (types != null)
{
keyType = types[0];
valueType = types[1];
}
return createMapType(keyType, valueType);
}
/**
* Create a map type
*
* @param keyType the key type
* @param valueType the value type
* @return the map type
*/
public TableMetaType createMapType(TypeInfo keyType, TypeInfo valueType)
{
String name = Map.class.getName();
MetaType[] itemTypes = { resolve(keyType), resolve(valueType) };
CompositeMetaType entryType = createMapEntryType(itemTypes);
return new ImmutableTableMetaType(name, name, entryType, MAP_INDEX_NAMES);
}
/**
* Create a map entry type
*
* @param itemTypes the item types
* @return the map entry type
*/
public static CompositeMetaType createMapEntryType(MetaType[] itemTypes)
{
String entryName = Map.Entry.class.getName();
return new ImmutableCompositeMetaType(entryName, entryName, MAP_ITEM_NAMES, MAP_ITEM_NAMES, itemTypes);
}
/**
* Generate a bean metatype
*
* @param typeInfo the type info
* @return the metatype
*/
public CompositeMetaType generateBean(ClassInfo typeInfo)
{
BeanInfo beanInfo = configuration.getBeanInfo(typeInfo);
MutableCompositeMetaType result = new MutableCompositeMetaType(typeInfo.getName(), typeInfo.getName());
typeInfo.setAttachment(MetaType.class.getName(), result);
Set<String> keys = null;
Set<PropertyInfo> properties = beanInfo.getProperties();
if (properties != null && properties.size() > 0)
{
for (PropertyInfo property : properties)
{
// Do we ignore this property?
CompositeValue compositeValue = property.getUnderlyingAnnotation(CompositeValue.class);
if (compositeValue != null && compositeValue.ignore())
continue;
String name = property.getName();
if ("class".equals(name) == false)
{
if (compositeValue != null)
{
String compositeValueName = compositeValue.name();
if (MetaTypeConstants.DEFAULT.equals(compositeValueName) == false)
name = compositeValueName;
}
TypeInfo itemTypeInfo = property.getType();
MetaType metaType = resolve(itemTypeInfo);
result.addItem(name, name, metaType);
if (property.isAnnotationPresent(CompositeKey.class))
{
if (keys == null)
keys = new LinkedHashSet<String>();
keys.add(name);
}
}
}
}
if (keys != null)
result.setKeys(keys);
result.freeze();
return result;
}
/**
* Check for builders
*
* @param typeInfo the type info
* @return the meta type when it is special
*/
@SuppressWarnings("deprecation")
public MetaType isBuilder(TypeInfo typeInfo)
{
MetaTypeBuilder builder = null;
synchronized (builders)
{
WeakReference<MetaTypeBuilder> weak = builders.get(typeInfo.getType());
if (weak != null)
builder = weak.get();
}
if (builder == null)
return null;
return builder.buildMetaType();
}
/**
* Check for generic
*
* @param typeInfo the type info
* @return the meta type when it is generic
*/
public GenericMetaType isGeneric(TypeInfo typeInfo)
{
if (typeInfo instanceof ClassInfo == false)
return null;
ClassInfo classInfo = (ClassInfo) typeInfo;
Generic generic = classInfo.getUnderlyingAnnotation(Generic.class);
if (generic != null)
return new GenericMetaType(typeInfo.getName(), typeInfo.getName());
return null;
}
}