package org.fusesource.restygwt.rebind; import java.util.Collection; import java.util.List; import java.util.Map; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonSubTypes.Type; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.databind.annotation.JsonTypeIdResolver; import com.fasterxml.jackson.databind.jsontype.TypeIdResolver; import org.fusesource.restygwt.rebind.JsonEncoderDecoderClassCreator.Subtype; import org.fusesource.restygwt.rebind.util.JsonTypeInfoIdVisitor; import com.google.gwt.core.ext.GeneratorContext; import com.google.gwt.core.ext.TreeLogger; import com.google.gwt.core.ext.UnableToCompleteException; import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.thirdparty.guava.common.collect.Lists; import static org.fusesource.restygwt.rebind.util.AnnotationUtils.*; public class PossibleTypesVisitor extends JsonTypeInfoIdVisitor<List<Subtype>, UnableToCompleteException> { private GeneratorContext context; private JClassType classType; private boolean isLeaf; private TreeLogger logger; private Collection<JsonSubTypes.Type> types; public PossibleTypesVisitor(GeneratorContext context, JClassType classType, final boolean isLeaf, TreeLogger logger, final Collection<Type> types) { this.context = context; this.classType = classType; this.isLeaf = isLeaf; this.logger = logger; this.types = types; } @Override public List<Subtype> visitClass() throws UnableToCompleteException { return getPossibleTypesForClass(context, classType, Id.CLASS, isLeaf, logger, types); } @Override public List<Subtype> visitMinClass() throws UnableToCompleteException { return getPossibleTypesForClass(context, classType, Id.MINIMAL_CLASS, isLeaf, logger, types); } @Override public List<Subtype> visitCustom() throws UnableToCompleteException { return getPossibleTypesForOther(context, classType, isLeaf, logger, types); } @Override public List<Subtype> visitName() throws UnableToCompleteException { return getPossibleTypesForOther(context, classType, isLeaf, logger, types); } @Override public List<Subtype> visitNone() throws UnableToCompleteException { logger.log(BaseSourceCreator.ERROR, "Id.NONE not supported"); throw new UnableToCompleteException(); } @Override public List<Subtype> visitDefault() throws UnableToCompleteException { return null; } protected List<Subtype> getPossibleTypesForOther(GeneratorContext context, JClassType classType, final boolean isLeaf, TreeLogger logger, final Collection<JsonSubTypes.Type> types) throws UnableToCompleteException { final List<Subtype> possibleTypes = Lists.newArrayList(); final JsonTypeIdResolver typeResolver = getClassAnnotation(classType, JsonTypeIdResolver.class); if (typeResolver != null) { Class<? extends TypeIdResolver> resolverClass = typeResolver.value(); RestyJsonTypeIdResolver restyResolver; if (RestyJsonTypeIdResolver.class.isAssignableFrom(resolverClass)) { try { restyResolver = (RestyJsonTypeIdResolver) resolverClass.newInstance(); } catch (Exception e) { logger.log(BaseSourceCreator.ERROR, "Could not acccess: " + resolverClass, e); throw new UnableToCompleteException(); } } else { restyResolver = JsonEncoderDecoderClassCreator.getRestyResolverClassMap(context, logger).get(resolverClass); if (restyResolver == null) { logger.log(BaseSourceCreator.ERROR, "Could not find RestyJsonTypeIdResolver for " + resolverClass + " did you forget to put <extend-configuration-property name=\"org.fusesource.restygwt.jsontypeidresolver\" value=\"<fully-qualified-class-implementing-RestyJsonTypeIdResolver>\"/> in your *.gwt.xml?"); throw new UnableToCompleteException(); } } for (Map.Entry<String, Class<?>> entry : restyResolver.getIdClassMap().entrySet()) { JClassType entryType = BaseSourceCreator.find(entry.getValue(), logger, context); if (!isLeaf || classType.equals(entryType)) possibleTypes.add(new Subtype(entry.getKey(), entryType)); } if (isLeaf && possibleTypes.size() == 0) { logger.log(BaseSourceCreator.ERROR, "Could not find entry in " + restyResolver.getClass().getName() + " for type: " + classType); throw new UnableToCompleteException(); } } else if (types != null) { for (JsonSubTypes.Type type : types) { if (type.name() != null && !type.name().isEmpty()) { JClassType typeClass = BaseSourceCreator.find(type.value(), logger, context); if (!isLeaf || classType.equals(typeClass)) possibleTypes.add(new Subtype(type.name(), typeClass)); } else { JsonTypeName nameAnnotation = type.value().getAnnotation(JsonTypeName.class); if (nameAnnotation == null || nameAnnotation.value() == null || nameAnnotation.value().isEmpty()) { logger.log(BaseSourceCreator.ERROR, "Cannot find @JsonTypeName annotation for type: " + type.value()); throw new UnableToCompleteException(); } JClassType typeClass = BaseSourceCreator.find(type.value(), logger, context); if (!isLeaf || classType.equals(typeClass)) possibleTypes.add(new Subtype(nameAnnotation.value(), typeClass)); } } if (isLeaf && possibleTypes.size() == 0) { logger.log(BaseSourceCreator.ERROR, "Could not find @JsonSubTypes entry for type: " + classType); throw new UnableToCompleteException(); } } else { logger.log(BaseSourceCreator.ERROR, "Cannot find required subtype resolution for type: " + classType); throw new UnableToCompleteException(); } return possibleTypes; } protected List<Subtype> getPossibleTypesForClass(GeneratorContext context, JClassType classType, final Id id, final boolean isLeaf, TreeLogger logger, final Collection<JsonSubTypes.Type> types) throws UnableToCompleteException { final List<Subtype> possibleTypes = Lists.newArrayList(); List<JClassType> resolvedSubtypes = Lists.newArrayList(); if (types != null) { for (JsonSubTypes.Type type : types) { JClassType typeClass = BaseSourceCreator.find(type.value(), logger, context); if (!isLeaf || classType.equals(typeClass)) resolvedSubtypes.add(typeClass); } } else { for (JClassType typeClass : context.getTypeOracle().getTypes()) { if (!typeClass.isAbstract() && typeClass.isAssignableTo(classType)) resolvedSubtypes.add(typeClass); } } for (JClassType typeClass : resolvedSubtypes) possibleTypes.add(new Subtype(id == Id.CLASS ? typeClass.getQualifiedSourceName() : "." + typeClass.getSimpleSourceName(), typeClass)); return possibleTypes; } }