/** * Copyright © 2006-2016 Web Cohesion (info@webcohesion.com) * * Licensed 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 com.webcohesion.enunciate.modules.jackson.model.util; import com.webcohesion.enunciate.javac.decorations.DecoratedProcessingEnvironment; import com.webcohesion.enunciate.javac.decorations.type.DecoratedDeclaredType; import com.webcohesion.enunciate.javac.decorations.type.TypeMirrorUtils; import com.webcohesion.enunciate.modules.jackson.EnunciateJacksonContext; import com.webcohesion.enunciate.modules.jackson.model.adapters.AdapterType; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeVisitor; import javax.lang.model.util.Types; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * A decorated map type. * * @author Ryan Heaton */ public class MapType extends DecoratedDeclaredType { static final String PROPERTY_MAP_TYPES = "com.webcohesion.enunciate.modules.jackson1.model.util.MapType#PROPERTY_MAP_TYPES"; private TypeMirror keyType; private TypeMirror valueType; private DeclaredType originalType; private MapType(DeclaredType mapType, DecoratedProcessingEnvironment env) { super(mapType, env); this.originalType = mapType; } /** * Finds the map type for the specified type mirror, if it exists. * * @param typeMirror The type mirror. * @param context The context * @return The map type or null. */ public static MapType findMapType(TypeMirror typeMirror, EnunciateJacksonContext context) { if (!(typeMirror instanceof DeclaredType)) { return null; } DeclaredType declaredType = (DeclaredType) typeMirror; TypeElement element = (TypeElement) declaredType.asElement(); if (element == null) { return null; } else { String typeSignature = declaredType.toString(); @SuppressWarnings ( "unchecked" ) Map<String, MapType> mapTypes = (Map<String, MapType>) context.getContext().getProperty(PROPERTY_MAP_TYPES); if (mapTypes == null) { mapTypes = new HashMap<String, MapType>(); context.getContext().setProperty(PROPERTY_MAP_TYPES, mapTypes); } MapType mapType = mapTypes.get(typeSignature); if (mapType != null) { return mapType; } else { DeclaredType declaredMapType = findMapTypeDeclaration(declaredType, context); if (declaredMapType == null) { return null; } MapType newMapType = new MapType(declaredType, context.getContext().getProcessingEnvironment()); mapTypes.put(typeSignature, newMapType); TypeMirror keyType = null; TypeMirror valueType = null; List<? extends TypeMirror> typeArgs = declaredMapType.getTypeArguments(); if ((typeArgs != null) && (typeArgs.size() == 2)) { Iterator<? extends TypeMirror> argIt = typeArgs.iterator(); keyType = argIt.next(); valueType = argIt.next(); } if ((keyType == null) || (valueType == null)) { TypeMirror objectType = TypeMirrorUtils.objectType(context.getContext().getProcessingEnvironment()); keyType = objectType; valueType = objectType; } TypeMirror mapKeyType = findMapType(keyType, context); newMapType.keyType = mapKeyType == null ? keyType : mapKeyType; TypeMirror mapValueType = findMapType(valueType, context); newMapType.valueType = mapValueType == null ? valueType : mapValueType; return newMapType; } } } public static DeclaredType findMapTypeDeclaration(TypeMirror typeMirror, EnunciateJacksonContext context) { if (!(typeMirror instanceof DeclaredType)) { return null; } DeclaredType declaredType = (DeclaredType) typeMirror; TypeElement element = (TypeElement) declaredType.asElement(); String fqn = element.getQualifiedName().toString(); if (Map.class.getName().equals(fqn)) { return declaredType; } AdapterType adapterType = JacksonUtil.findAdapterType(element, context); if (adapterType != null) { return findMapTypeDeclaration(adapterType.getAdaptingType(), context); } DeclaredType mapType = null; Types typeUtils = context.getContext().getProcessingEnvironment().getTypeUtils(); List<? extends TypeMirror> supers = typeUtils.directSupertypes(declaredType); for (TypeMirror superInterface : supers) { mapType = findMapTypeDeclaration(superInterface, context); if (mapType != null) { break; } } return mapType; } /** * The key type associated with this map type. * * @return The key type associated with this map type. */ public TypeMirror getKeyType() { return keyType; } /** * The key type associated with this map type. * * @return The key type associated with this map type. */ public TypeMirror getValueType() { return valueType; } /** * The original map type. * * @return The original map type. */ public DeclaredType getOriginalType() { return originalType; } public boolean isMap() { return true; } @Override public TypeKind getKind() { return this.originalType.getKind(); } @Override public <R, P> R accept(TypeVisitor<R, P> v, P p) { return this.originalType.accept(v, p); } }