/** * 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.jaxb.model.types; import com.webcohesion.enunciate.EnunciateException; import com.webcohesion.enunciate.modules.jaxb.EnunciateJaxbContext; import com.webcohesion.enunciate.modules.jaxb.model.TypeDefinition; import com.webcohesion.enunciate.modules.jaxb.model.adapters.AdapterType; import com.webcohesion.enunciate.modules.jaxb.model.util.JAXBUtil; import com.webcohesion.enunciate.modules.jaxb.model.util.MapType; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.lang.model.type.*; import javax.lang.model.util.SimpleTypeVisitor6; import java.util.LinkedList; /** * Utility visitor for discovering the xml types of type mirrors. * * @author Ryan Heaton */ public class XmlTypeVisitor extends SimpleTypeVisitor6<XmlType, XmlTypeVisitor.Context> { @Override protected XmlType defaultAction(TypeMirror typeMirror, Context context) { throw new EnunciateException(typeMirror + " is not recognized as an XML type."); } @Override public XmlType visitPrimitive(PrimitiveType primitiveType, Context context) { if (context.isInArray() && (primitiveType.getKind() == TypeKind.BYTE)) { //special case for byte[] return KnownXmlType.BASE64_BINARY; } else { return new XmlPrimitiveType(primitiveType); } } @Override public XmlType visitDeclared(DeclaredType declaredType, Context context) { Element declaredElement = declaredType.asElement(); String fqn = declaredElement instanceof TypeElement ? ((TypeElement) declaredElement).getQualifiedName().toString() : declaredType.toString(); if (context.getStack().contains(fqn)) { return KnownXmlType.ANY_TYPE; //break the recursion. } context.getStack().push(fqn); try { AdapterType adapterType = JAXBUtil.findAdapterType(declaredElement, context.getContext()); if (adapterType != null) { adapterType.getAdaptingType().accept(this, context); } else { MapType mapType = MapType.findMapType(declaredType, context.getContext()); if (mapType != null) { XmlType keyType = mapType.getKeyType().accept(this, new Context(context.getContext(), false, false, context.stack)); XmlType valueType = mapType.getValueType().accept(this, new Context(context.getContext(), false, false, context.stack)); return new MapXmlType(keyType, valueType); } else { switch (declaredElement.getKind()) { case ENUM: case CLASS: XmlType knownType = context.getContext().getKnownType(declaredElement); if (knownType != null) { return knownType; } else { //type not known, not specified. Last chance: look for the type definition. TypeDefinition typeDefinition = context.getContext().findTypeDefinition(declaredElement); if (typeDefinition != null) { return new XmlClassType(typeDefinition); } } break; } } } return KnownXmlType.ANY_TYPE; } finally { context.getStack().pop(); } } @Override public XmlType visitArray(ArrayType arrayType, Context context) { if (context.isInArray()) { throw new UnsupportedOperationException("Enunciate JAXB support doesn't handle multi-dimensional arrays."); } return arrayType.getComponentType().accept(this, context); } @Override public XmlType visitTypeVariable(TypeVariable typeVariable, Context context) { TypeMirror bound = typeVariable.getUpperBound(); if (bound == null) { return KnownXmlType.ANY_TYPE; } else { return bound.accept(this, context); } } @Override public XmlType visitWildcard(WildcardType wildcardType, Context context) { TypeMirror bound = wildcardType.getExtendsBound(); if (bound == null) { return KnownXmlType.ANY_TYPE; } else { return bound.accept(this, context); } } public static class Context { private final EnunciateJaxbContext context; private final boolean inArray; private final boolean inCollection; private final LinkedList<String> stack; public Context(EnunciateJaxbContext context, boolean inArray, boolean inCollection, LinkedList<String> stack) { this.context = context; this.inArray = inArray; this.inCollection = inCollection; this.stack = stack; } public EnunciateJaxbContext getContext() { return context; } public boolean isInArray() { return inArray; } public boolean isInCollection() { return inCollection; } public LinkedList<String> getStack() { return stack; } } }