/** * */ package net.paoding.rose.jade.plugin.sql.mapper; import java.lang.annotation.Annotation; import java.lang.reflect.GenericDeclaration; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import net.paoding.rose.jade.annotation.SQLParam; import net.paoding.rose.jade.plugin.sql.GenericDAO; import net.paoding.rose.jade.plugin.sql.Plum; import net.paoding.rose.jade.plugin.sql.annotations.IgnoreNull; import net.paoding.rose.jade.statement.StatementMetaData; /** * @author Alan.Geng[[email protected]] * */ public class OperationMapper extends AbstractMapper<StatementMetaData> implements IOperationMapper { private IEntityMapper targetEntityMapper; private List<IParameterMapper> parameters; private EntityMapperManager entityMapperManager; private Class<?> entityType; private Class<?> primaryKeyType; private IgnoreNull ignoreNull; public static final List<IParameterMapper> NO_PARAMETER = Collections.unmodifiableList(new ArrayList<IParameterMapper>()); public OperationMapper(StatementMetaData original) { super(original); } @Override public IEntityMapper getTargetEntityMapper() { return targetEntityMapper; } @Override protected void doMap() { ignoreNull = original.getAnnotation(IgnoreNull.class); mapGenericEntityType(); mapTargetEntityMapper(); mapParameters(); } protected void mapGenericEntityType() { entityType = original.getDAOMetaData().resolveTypeVariable(GenericDAO.class, "E"); primaryKeyType = original.getDAOMetaData().resolveTypeVariable(GenericDAO.class, "ID"); if(entityType == null) { throw new MappingException("Cannot find the generic type."); } } protected void mapTargetEntityMapper() { targetEntityMapper = entityMapperManager.createGet((Class<?>) entityType); } protected void mapParameters() { Method method = original.getMethod(); Type[] parameterTypes = method.getGenericParameterTypes(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); if(parameterTypes == null || parameterTypes.length == 0) { parameters = NO_PARAMETER; if((getName() == OPERATION_INSERT || getName() == OPERATION_DELETE || getName() == OPERATION_UPDATE) && parameters == NO_PARAMETER) { // 写操作必须存在参数 throw new MappingException("The insert operation must has least 1 parameters."); } return; } parameters = new ArrayList<IParameterMapper>(parameterAnnotations.length); for(int i = 0; i < parameterAnnotations.length; i++) { // TODO: 是否要进行参数类型检查? IParameterMapper parameterMapper = createParameterMapper(resolveParameterType(parameterTypes[i]), parameterAnnotations[i], i); if(parameterMapper != null) { parameters.add(parameterMapper); } } } protected Class<?> resolveParameterType(Type type) { if(type instanceof Class) { return (Class<?>) type; } else if(type instanceof ParameterizedType && ((ParameterizedType) type).getRawType() instanceof Class && Collection.class.isAssignableFrom((Class<?>) ((ParameterizedType) type).getRawType())) { // GenericDAO中的集合泛型参数 return resolveParameterType(((ParameterizedType) type).getActualTypeArguments()[0]); } else if(type instanceof TypeVariable) { // GenericDAO中的非集合泛型参数,或许是主键或许是实体。 GenericDeclaration genericDeclaration = ((TypeVariable<?>) type).getGenericDeclaration(); if(genericDeclaration == GenericDAO.class) { Type[] bounds = ((TypeVariable<?>) type).getBounds(); if(bounds[0] == Object.class) { String name = ((TypeVariable<?>) type).getName(); if("E".equals(name)) { return entityType; } else if("ID".equals(name)) { return primaryKeyType; } else { throw new MappingException("Unknown type variable \"" + type + "\"."); } } else { return resolveParameterType(bounds[0]); } } else { throw new MappingException("Unsupported generic declaration \"" + genericDeclaration + "\"."); } } throw new MappingException("Unknown type \"" + type + "\"."); } protected Type[] getCollectionTypeBounds(Class<?> collectionType) { TypeVariable<?>[] typeParameters = collectionType.getTypeParameters(); if(typeParameters.length == 0) { throw new MappingException("The generic type of collection must be specified."); } TypeVariable<?> typeVariable = collectionType.getTypeParameters()[0]; return typeVariable.getBounds(); } protected IParameterMapper createParameterMapper(Class<?> type, Annotation[] annotations, int index) { SQLParam sp = null; if(annotations != null && annotations.length > 0) { for(Annotation annotation : annotations) { if(annotation.annotationType().equals(SQLParam.class)) { sp = (SQLParam) annotation; break; } } } IParameterMapper param = null; if(isExpandableParameterType(type)) { param = new ExpandableParameterMapper(this, sp, (Class<?>) type); ((ExpandableParameterMapper) param).setEntityMapperManager(entityMapperManager); } else { param = new ParameterMapper(this, sp, type, annotations); } param.map(); return param; } protected boolean isExpandableParameterType(Class<?> type) { if(entityType.isAssignableFrom(type)) { return true; } return false; } @Override public boolean containsParameter() { return parameters != NO_PARAMETER; } @Override protected String generateOriginalName() { return original.getMethod().getName(); } @Override public String generateName(String source) { for(int i = 0; i < OPERATION_PREFIX.length; i++) { String[] prefixs = OPERATION_PREFIX[i]; for(int j = 0; j < prefixs.length; j++) { if(source.startsWith(prefixs[j])) { return OPERATION_KEYS[i]; } } } throw new MappingException("Unsupported method \"" + source + "\"."); } public void setEntityMapperManager(EntityMapperManager entityMapperManager) { this.entityMapperManager = entityMapperManager; } @Override public List<IParameterMapper> getParameters() { return parameters; } public Class<?> getPrimaryKeyType() { return primaryKeyType; } public Class<?> getEntityType() { return entityType; } @Override public boolean isIgnoreNull() { return ignoreNull == null ? true : Plum.DEFAULT_IGNORE_NULL; } @Override public IgnoreNull getIgnoreNull() { return ignoreNull; } }