package org.skyscreamer.yoga.metadata; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.skyscreamer.yoga.selector.CoreSelector; import org.skyscreamer.yoga.selector.Property; import org.skyscreamer.yoga.util.NameUtil; public class DefaultMetaDataRegistry implements MetaDataRegistry { private Map<String, Class<?>> _typeMappings = new HashMap<String, Class<?>>(); private Map<Class<?>, String> _typeToStringMap = new HashMap<Class<?>, String>(); private String rootMetaDataUrl; private CoreSelector _coreSelector; public void setCoreSelector( CoreSelector coreSelector ) { this._coreSelector = coreSelector; } public void setRootMetaDataUrl( String rootMetaDataUrl ) { this.rootMetaDataUrl = rootMetaDataUrl; } public void setTypeMappings( Map<String, Class<?>> map ) { this._typeMappings = map; _typeToStringMap.clear(); for ( Entry<String, Class<?>> entry : map.entrySet() ) { _typeToStringMap.put( entry.getValue(), entry.getKey() ); } } public void registerTypeMapping( String name, Class<?> type ) { _typeMappings.put( name, type ); _typeToStringMap.put( type, name ); } public void registerClasses( Class<?> ... types ) { setRegisteredClasses( types ); } public void registerClasses( Collection<Class<?>> types ) { setRegisteredClasses( types.toArray( new Class<?>[types.size()] ) ); } public void setRegisteredClasses( Class<?> ... types ) { for( Class<?> type : types ) { registerTypeMapping( NameUtil.getName( type ), type ); } } @Override public Collection<String> getTypes() { return _typeMappings.keySet(); } private Class<?> getTypeForName( String name ) { return _typeMappings.get( name ); } /** * given a type, get a name. This takes subclassing into consideration. For * now, this will return the first subclass match to the type, not the closest */ private String getNameForType( Class<?> type ) { if ( _typeToStringMap.containsKey( type ) ) { return _typeToStringMap.get( type ); } for ( Entry<String, Class<?>> entry : _typeMappings.entrySet() ) { if ( entry.getValue().isAssignableFrom( type ) ) { return entry.getKey(); } } return null; } @Override public TypeMetaData getMetaData( String name, String suffix ) { return getMetaData( getTypeForName( name ), suffix ); } private TypeMetaData getMetaData( Class<?> type, String suffix ) { TypeMetaData result = new TypeMetaData(); result.setName( NameUtil.getFormalName( type ) ); addFields( type, suffix, result ); return result; } protected <T> void addFields( Class<T> type, String suffix, TypeMetaData result ) { Map<String, Property<T>> allFields = _coreSelector.getAllPossibleFieldMap( type ); Set<String> coreFieldName = new HashSet<String>(); for (Property<T> property : _coreSelector.getSelectedFields( type )) { coreFieldName.add( property.name() ); } for ( Property<T> property : allFields.values() ) { Method readMethod = property.getReadMethod(); Class<?> propertyType = readMethod.getReturnType(); PropertyMetaData propertyMetaData = new PropertyMetaData(); String name = property.name(); propertyMetaData.setName( name ); propertyMetaData.setIsCore( coreFieldName.contains( name ) ); if ( property.isPrimitive() ) { propertyMetaData.setType( propertyType == String.class ? "String" : propertyType.getName() ); } else if ( Iterable.class.isAssignableFrom( propertyType ) || propertyType.isArray() ) { Class<?> collectionValueType = getCollectionType( readMethod, propertyType ); String typeName; if ( collectionValueType == null ) { typeName = "Collection"; } else { typeName = "Collection<" + NameUtil.getFormalName(collectionValueType) + ">"; } propertyMetaData.setType( typeName ); addHref( propertyMetaData, collectionValueType, suffix ); } else { propertyMetaData.setType( NameUtil.getFormalName( propertyType ) ); addHref( propertyMetaData, propertyType, suffix ); } result.getPropertyMetaData().add( propertyMetaData ); } } protected Class<?> getCollectionType( Method readMethod, Class<?> propertyType ) { Class<?> collectionValueType = null; if ( propertyType.isArray() ) { collectionValueType = propertyType.getComponentType(); } else { Type genericReturnType = readMethod.getGenericReturnType(); if ( genericReturnType instanceof ParameterizedType ) { ParameterizedType zType = (ParameterizedType) genericReturnType; collectionValueType = (Class<?>) zType.getActualTypeArguments()[0]; } } return collectionValueType; } @Override public String getMetadataHref(Class<?> propertyType, String suffix) { String nameForType = getNameForType( propertyType ); if ( nameForType != null ) { return rootMetaDataUrl + nameForType + "." + suffix; } else { return null; } } protected void addHref( PropertyMetaData propertyMetaData, Class<?> propertyType, String suffix ) { String href = getMetadataHref( propertyType, suffix ); if (href != null) { propertyMetaData.setHref( href ); } } }