/* * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.oracle.truffle.dsl.processor.java.transform; import static com.oracle.truffle.dsl.processor.java.ElementUtils.findNearestEnclosingType; import static com.oracle.truffle.dsl.processor.java.ElementUtils.getDeclaredTypes; import static com.oracle.truffle.dsl.processor.java.ElementUtils.getPackageName; import static com.oracle.truffle.dsl.processor.java.ElementUtils.getQualifiedName; import static com.oracle.truffle.dsl.processor.java.ElementUtils.getSuperTypes; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; import javax.lang.model.type.WildcardType; import javax.lang.model.util.AbstractAnnotationValueVisitor7; import javax.lang.model.util.ElementFilter; import com.oracle.truffle.dsl.processor.java.ElementUtils; import com.oracle.truffle.dsl.processor.java.model.CodeElementScanner; import com.oracle.truffle.dsl.processor.java.model.CodeExecutableElement; import com.oracle.truffle.dsl.processor.java.model.CodeImport; import com.oracle.truffle.dsl.processor.java.model.CodeTree; import com.oracle.truffle.dsl.processor.java.model.CodeTreeKind; import com.oracle.truffle.dsl.processor.java.model.CodeTypeElement; public final class OrganizedImports { private final Map<String, String> classImportUsage = new HashMap<>(); private final Map<String, Set<String>> autoImportCache = new HashMap<>(); private final CodeTypeElement topLevelClass; private OrganizedImports(CodeTypeElement topLevelClass) { this.topLevelClass = topLevelClass; } public static OrganizedImports organize(CodeTypeElement topLevelClass) { OrganizedImports organized = new OrganizedImports(topLevelClass); organized.organizeImpl(); return organized; } private void organizeImpl() { ImportTypeReferenceVisitor reference = new ImportTypeReferenceVisitor(); topLevelClass.accept(reference, null); } public String createTypeReference(Element enclosedElement, TypeMirror type) { switch (type.getKind()) { case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT: case SHORT: case INT: case LONG: case VOID: return ElementUtils.getSimpleName(type); case DECLARED: return createDeclaredTypeName(enclosedElement, (DeclaredType) type); case ARRAY: return createTypeReference(enclosedElement, ((ArrayType) type).getComponentType()) + "[]"; case WILDCARD: return createWildcardName(enclosedElement, (WildcardType) type); case TYPEVAR: return "?"; default: throw new RuntimeException("Unknown type specified " + type.getKind() + " mirror: " + type); } } public String createStaticFieldReference(Element enclosedElement, TypeMirror type, String fieldName) { return createStaticReference(enclosedElement, type, fieldName); } public String createStaticMethodReference(Element enclosedElement, TypeMirror type, String methodName) { return createStaticReference(enclosedElement, type, methodName); } private String createStaticReference(Element enclosedElement, TypeMirror type, String name) { // ambiguous import return createTypeReference(enclosedElement, type) + "." + name; } private String createWildcardName(Element enclosedElement, WildcardType type) { StringBuilder b = new StringBuilder(); if (type.getExtendsBound() != null) { b.append("? extends ").append(createTypeReference(enclosedElement, type.getExtendsBound())); } else if (type.getSuperBound() != null) { b.append("? super ").append(createTypeReference(enclosedElement, type.getExtendsBound())); } else { b.append("?"); } return b.toString(); } private String createDeclaredTypeName(Element enclosedElement, DeclaredType type) { String name = ElementUtils.fixECJBinaryNameIssue(type.asElement().getSimpleName().toString()); if (classImportUsage.containsKey(name)) { String qualifiedImport = classImportUsage.get(name); String qualifiedName = ElementUtils.getEnclosedQualifiedName(type); if (!qualifiedName.equals(qualifiedImport)) { name = ElementUtils.getQualifiedName(type); } } List<? extends TypeMirror> genericTypes = type.getTypeArguments(); if (genericTypes.size() == 0) { return name; } StringBuilder b = new StringBuilder(name); b.append("<"); for (int i = 0; i < genericTypes.size(); i++) { TypeMirror genericType = i < genericTypes.size() ? genericTypes.get(i) : null; if (genericType != null) { b.append(createTypeReference(enclosedElement, genericType)); } else { b.append("?"); } if (i < genericTypes.size() - 1) { b.append(", "); } } b.append(">"); return b.toString(); } public Set<CodeImport> generateImports() { Set<CodeImport> imports = new HashSet<>(); imports.addAll(generateImports(classImportUsage)); return imports; } private boolean needsImport(Element enclosed, TypeMirror importType) { String importPackagName = getPackageName(importType); TypeElement enclosedElement = findNearestEnclosingType(enclosed); if (importPackagName == null) { return false; } else if (importPackagName.equals("java.lang")) { return false; } else if (importPackagName.equals(getPackageName(topLevelClass)) && ElementUtils.isTopLevelClass(importType)) { return false; // same package name -> no import } String enclosedElementId = ElementUtils.getUniqueIdentifier(enclosedElement.asType()); Set<String> autoImportedTypes = autoImportCache.get(enclosedElementId); if (autoImportedTypes == null) { List<Element> elements = ElementUtils.getElementHierarchy(enclosedElement); autoImportedTypes = new HashSet<>(); for (Element element : elements) { if (element.getKind().isClass()) { collectSuperTypeImports((TypeElement) element, autoImportedTypes); collectInnerTypeImports((TypeElement) element, autoImportedTypes); } } autoImportCache.put(enclosedElementId, autoImportedTypes); } String qualifiedName = getQualifiedName(importType); if (autoImportedTypes.contains(qualifiedName)) { return false; } return true; } private static Set<CodeImport> generateImports(Map<String, String> symbols) { TreeSet<CodeImport> importObjects = new TreeSet<>(); for (String symbol : symbols.keySet()) { String packageName = symbols.get(symbol); if (packageName != null) { importObjects.add(new CodeImport(packageName, symbol, false)); } } return importObjects; } private static void collectInnerTypeImports(TypeElement e, Set<String> autoImportedTypes) { autoImportedTypes.add(getQualifiedName(e)); for (TypeElement innerClass : ElementFilter.typesIn(e.getEnclosedElements())) { collectInnerTypeImports(innerClass, autoImportedTypes); } } private static void collectSuperTypeImports(TypeElement e, Set<String> autoImportedTypes) { List<TypeElement> superTypes = getSuperTypes(e); for (TypeElement superType : superTypes) { List<TypeElement> declaredTypes = getDeclaredTypes(superType); for (TypeElement declaredType : declaredTypes) { if (!superTypes.contains(declaredType)) { autoImportedTypes.add(getQualifiedName(declaredType)); } } } } private abstract static class TypeReferenceVisitor extends CodeElementScanner<Void, Void> { @Override public void visitTree(CodeTree e, Void p, Element enclosing) { if (e.getCodeKind() == CodeTreeKind.STATIC_FIELD_REFERENCE) { visitStaticFieldReference(enclosing, e.getType(), e.getString()); } else if (e.getCodeKind() == CodeTreeKind.STATIC_METHOD_REFERENCE) { visitStaticMethodReference(enclosing, e.getType(), e.getString()); } else if (e.getType() != null) { visitTypeReference(enclosing, e.getType()); } super.visitTree(e, p, enclosing); } @Override public Void visitExecutable(CodeExecutableElement e, Void p) { visitAnnotations(e, e.getAnnotationMirrors()); if (e.getReturnType() != null) { visitTypeReference(e, e.getReturnType()); } for (TypeMirror type : e.getThrownTypes()) { visitTypeReference(e, type); } return super.visitExecutable(e, p); } @Override public Void visitType(CodeTypeElement e, Void p) { visitAnnotations(e, e.getAnnotationMirrors()); visitTypeReference(e, e.getSuperclass()); for (TypeMirror type : e.getImplements()) { visitTypeReference(e, type); } return super.visitType(e, p); } private void visitAnnotations(Element enclosingElement, List<? extends AnnotationMirror> mirrors) { for (AnnotationMirror mirror : mirrors) { visitAnnotation(enclosingElement, mirror); } } public void visitAnnotation(Element enclosingElement, AnnotationMirror e) { visitTypeReference(enclosingElement, e.getAnnotationType()); if (!e.getElementValues().isEmpty()) { Map<? extends ExecutableElement, ? extends AnnotationValue> values = e.getElementValues(); Set<? extends ExecutableElement> methodsSet = values.keySet(); List<ExecutableElement> methodsList = new ArrayList<>(); for (ExecutableElement method : methodsSet) { if (values.get(method) == null) { continue; } methodsList.add(method); } for (int i = 0; i < methodsList.size(); i++) { AnnotationValue value = values.get(methodsList.get(i)); visitAnnotationValue(enclosingElement, value); } } } public void visitAnnotationValue(Element enclosingElement, AnnotationValue e) { e.accept(new AnnotationValueReferenceVisitor(enclosingElement), null); } private class AnnotationValueReferenceVisitor extends AbstractAnnotationValueVisitor7<Void, Void> { private final Element enclosingElement; AnnotationValueReferenceVisitor(Element enclosedElement) { this.enclosingElement = enclosedElement; } @Override public Void visitBoolean(boolean b, Void p) { return null; } @Override public Void visitByte(byte b, Void p) { return null; } @Override public Void visitChar(char c, Void p) { return null; } @Override public Void visitDouble(double d, Void p) { return null; } @Override public Void visitFloat(float f, Void p) { return null; } @Override public Void visitInt(int i, Void p) { return null; } @Override public Void visitLong(long i, Void p) { return null; } @Override public Void visitShort(short s, Void p) { return null; } @Override public Void visitString(String s, Void p) { return null; } @Override public Void visitType(TypeMirror t, Void p) { visitTypeReference(enclosingElement, t); return null; } @Override public Void visitEnumConstant(VariableElement c, Void p) { visitTypeReference(enclosingElement, c.asType()); return null; } @Override public Void visitAnnotation(AnnotationMirror a, Void p) { TypeReferenceVisitor.this.visitAnnotation(enclosingElement, a); return null; } @Override public Void visitArray(List<? extends AnnotationValue> vals, Void p) { for (int i = 0; i < vals.size(); i++) { TypeReferenceVisitor.this.visitAnnotationValue(enclosingElement, vals.get(i)); } return null; } } @Override public Void visitVariable(VariableElement f, Void p) { visitAnnotations(f, f.getAnnotationMirrors()); visitTypeReference(f, f.asType()); return super.visitVariable(f, p); } @Override public void visitImport(CodeImport e, Void p) { } public abstract void visitTypeReference(Element enclosedType, TypeMirror type); public abstract void visitStaticMethodReference(Element enclosedType, TypeMirror type, String elementName); public abstract void visitStaticFieldReference(Element enclosedType, TypeMirror type, String elementName); } private class ImportTypeReferenceVisitor extends TypeReferenceVisitor { @Override public void visitStaticFieldReference(Element enclosedType, TypeMirror type, String elementName) { visitTypeReference(enclosedType, type); } @Override public void visitStaticMethodReference(Element enclosedType, TypeMirror type, String elementName) { visitTypeReference(enclosedType, type); } @Override public void visitTypeReference(Element enclosedType, TypeMirror type) { if (type != null) { switch (type.getKind()) { case BOOLEAN: case BYTE: case CHAR: case DOUBLE: case FLOAT: case SHORT: case INT: case LONG: case VOID: return; case DECLARED: if (needsImport(enclosedType, type)) { DeclaredType declard = (DeclaredType) type; registerSymbol(classImportUsage, ElementUtils.getEnclosedQualifiedName(declard), ElementUtils.getDeclaredName(declard, false)); } for (TypeMirror argument : ((DeclaredType) type).getTypeArguments()) { visitTypeReference(enclosedType, argument); } return; case ARRAY: visitTypeReference(enclosedType, ((ArrayType) type).getComponentType()); return; case WILDCARD: WildcardType wildcard = (WildcardType) type; if (wildcard.getExtendsBound() != null) { visitTypeReference(enclosedType, wildcard.getExtendsBound()); } else if (wildcard.getSuperBound() != null) { visitTypeReference(enclosedType, wildcard.getSuperBound()); } return; case TYPEVAR: return; default: throw new RuntimeException("Unknown type specified " + type.getKind() + " mirror: " + type); } } } private void registerSymbol(Map<String, String> symbolUsage, String elementQualifiedName, String elementName) { if (symbolUsage.containsKey(elementName)) { String otherQualifiedName = symbolUsage.get(elementName); if (otherQualifiedName == null) { // already registered ambiguous return; } if (!otherQualifiedName.equals(elementQualifiedName)) { symbolUsage.put(elementName, null); } } else { symbolUsage.put(elementName, elementQualifiedName); } } } }