| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.netbeans.modules.refactoring.java.plugins; |
| |
| import com.sun.source.doctree.DocCommentTree; |
| import com.sun.source.tree.*; |
| import com.sun.source.util.SourcePositions; |
| import com.sun.source.util.TreePath; |
| import com.sun.source.util.Trees; |
| import java.util.*; |
| import java.util.logging.Logger; |
| import javax.lang.model.SourceVersion; |
| import javax.lang.model.element.*; |
| import javax.lang.model.type.ArrayType; |
| import javax.lang.model.type.DeclaredType; |
| import javax.lang.model.type.ExecutableType; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import javax.lang.model.util.ElementFilter; |
| import javax.lang.model.util.Types; |
| import org.netbeans.api.java.source.*; |
| import org.netbeans.modules.refactoring.api.Problem; |
| import org.netbeans.modules.refactoring.java.RefactoringUtils; |
| import org.netbeans.modules.refactoring.java.api.IntroduceLocalExtensionRefactoring; |
| import org.netbeans.modules.refactoring.java.api.IntroduceLocalExtensionRefactoring.Equality; |
| import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils; |
| import org.netbeans.modules.refactoring.java.spi.RefactoringVisitor; |
| import org.netbeans.modules.refactoring.java.spi.ToPhaseException; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.util.MapFormat; |
| import org.openide.util.NbBundle; |
| import org.openide.util.Utilities; |
| |
| /** |
| * |
| * @author Ralph Ruijs |
| */ |
| public class IntroduceLocalExtensionTransformer extends RefactoringVisitor { |
| |
| private static final Logger LOG = Logger.getLogger(IntroduceLocalExtensionTransformer.class.getName()); |
| private static final String DELEGATE = "delegate"; //NOI18N |
| private final IntroduceLocalExtensionRefactoring refactoring; |
| private Problem problem; |
| private String fqn; |
| private boolean initialized; |
| private final Map<ElementHandle<Element>, String[]> getterSetterMap; |
| |
| public IntroduceLocalExtensionTransformer(IntroduceLocalExtensionRefactoring refactoring) { |
| this.refactoring = refactoring; |
| this.initialized = false; |
| this.getterSetterMap = new HashMap<>(); |
| } |
| |
| @Override |
| public void setWorkingCopy(WorkingCopy workingCopy) throws ToPhaseException { |
| if (!initialized) { |
| FileObject origin = SourceUtils.getFile(refactoring.getRefactoringSource().lookup(TreePathHandle.class).getElementHandle(), workingCopy.getClasspathInfo()); |
| if (origin != null) { |
| SourceUtils.forceSource(workingCopy, origin); |
| } |
| } |
| super.setWorkingCopy(workingCopy); |
| } |
| |
| @Override |
| public Tree visitCompilationUnit(CompilationUnitTree node, Element p) { |
| if (!initialized) { |
| String packageName = refactoring.getPackageName(); |
| FileObject sourceRoot = refactoring.getSourceRoot(); |
| String name = refactoring.getNewName(); |
| boolean wrap = refactoring.getWrap(); |
| fqn = packageName + '.' + name; |
| |
| GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy); |
| CodeStyle cs = RefactoringUtils.getCodeStyle(workingCopy); |
| |
| TypeElement source = (TypeElement) refactoring.getRefactoringSource().lookup(TreePathHandle.class).getElementHandle().resolve(workingCopy); |
| |
| boolean noInterface = source.getKind() != ElementKind.INTERFACE; |
| boolean samePackage = packageName.contentEquals(workingCopy.getElements().getPackageOf(source).getQualifiedName()); |
| |
| List<TypeParameterTree> newTypeParams = new ArrayList<>(source.getTypeParameters().size()); |
| transformTypeParameters(source, source.getTypeParameters(), make, genUtils, newTypeParams); |
| |
| List<Tree> implementsList = wrap ? addInterfaces(source) : Collections.EMPTY_LIST; |
| |
| // add members |
| List<Tree> members = new ArrayList<>(); |
| Modifier needed; |
| if(wrap) { |
| if(samePackage) { |
| needed = null; |
| } else { |
| needed = Modifier.PUBLIC; |
| } |
| } else { |
| if(samePackage) { |
| needed = null; |
| } else { |
| needed = Modifier.PROTECTED; |
| } |
| } |
| addConstructors(source, members, needed); |
| |
| if (wrap && noInterface) { |
| Tree type = make.Type(source.asType()); |
| VariableTree field = make.Variable(make.Modifiers(EnumSet.of(Modifier.PRIVATE)), DELEGATE, type, null); //NOI18N |
| members.add(0, field); |
| addFields(source, cs, members); |
| } |
| |
| if (wrap && noInterface) { |
| createWrap(source, fqn, members); |
| addMembers(source, genUtils, members); |
| createEquals(source, genUtils, members); |
| } |
| |
| // create new class |
| Tree newClassTree; |
| final Set<Modifier> modifiers = new HashSet(source.getModifiers()); |
| modifiers.remove(Modifier.ABSTRACT); |
| modifiers.remove(Modifier.STATIC); |
| if(noInterface) { |
| newClassTree = make.Class( |
| make.Modifiers(modifiers), //classModifiersTree, |
| name, |
| newTypeParams, |
| wrap ? null : make.Type(source.asType()), //superClass, |
| implementsList, |
| members); |
| } else { |
| newClassTree = make.Interface( |
| make.Modifiers(modifiers), //classModifiersTree, |
| name, |
| newTypeParams, |
| Collections.singletonList(make.Type(source.asType())), //superClass, |
| members); |
| } |
| |
| // TODO: Useful javadoc for the class |
| // Doc javadoc = wc.getElementUtilities().javaDocFor(source); |
| // Comment comment = Comment.create(Comment.Style.JAVADOC, javadoc.getRawCommentText()); |
| // make.addComment(newClassTree, comment, true); |
| |
| String relativePath = fqn.replace('.', '/') + ".java"; // NOI18N |
| |
| CompilationUnitTree newCompilation = JavaPluginUtils.createCompilationUnit(sourceRoot, relativePath, newClassTree, workingCopy, make); |
| |
| workingCopy.rewrite(null, newCompilation); |
| initialized = true; |
| } |
| return super.visitCompilationUnit(node, p); |
| } |
| |
| private void addFields(TypeElement source, CodeStyle cs, List<Tree> members) throws IllegalStateException { |
| for (VariableElement field : ElementFilter.fieldsIn(workingCopy.getElements().getAllMembers(source))) { |
| if (!field.getModifiers().contains(Modifier.NATIVE) |
| && field.getModifiers().contains(Modifier.PUBLIC) |
| && field.getEnclosingElement().getKind() != ElementKind.INTERFACE |
| && !field.getEnclosingElement().equals(workingCopy.getElements().getTypeElement("java.lang.Object"))) { //NOI18N |
| if (field.getModifiers().contains(Modifier.FINAL) && field.getModifiers().contains(Modifier.STATIC)) { // Encapsulate? |
| VariableTree variable = make.Variable(field, make.QualIdent(field)); |
| members.add(0, variable); |
| } else { |
| ExecutableElement[] elements = new ExecutableElement[2]; |
| MethodTree[] createdGetterAndSetter = createGetterAndSetter(field, field.getModifiers(), cs, elements); |
| ElementHandle<Element> fieldHandle = ElementHandle.create((Element) field); |
| MethodTree getter = createdGetterAndSetter[0]; |
| MethodTree setter = createdGetterAndSetter[1]; |
| getterSetterMap.put(fieldHandle, new String[]{ |
| getter == null ? elements[0].getSimpleName().toString() : getter.getName().toString(), |
| setter == null ? elements[1].getSimpleName().toString() : setter.getName().toString()}); |
| if(getter != null) { |
| members.add(getter); |
| } |
| if(setter != null) { |
| members.add(setter); |
| } |
| } |
| } |
| } |
| } |
| |
| private MethodTree[] createGetterAndSetter(VariableElement field, Set<Modifier> useModifiers, CodeStyle cs, ExecutableElement[] elements) { |
| boolean staticMod = field.getModifiers().contains(Modifier.STATIC); |
| String getterName = CodeStyleUtils.computeGetterName(field.getSimpleName(), field.asType().getKind() == TypeKind.BOOLEAN, staticMod, cs); |
| String setterName = CodeStyleUtils.computeSetterName(field.getSimpleName(), staticMod, cs); |
| String fieldName = field.getSimpleName().toString(); |
| String longName = (staticMod ? "" : "this.") + DELEGATE + "." + fieldName;//NOI18N |
| String parName = staticMod ? "a" + getCapitalizedName(field) : Utilities.isJavaIdentifier(stripPrefix(fieldName)) ? stripPrefix(fieldName) : fieldName; //NOI18N |
| String getterBody = "{return " + DELEGATE + "." + fieldName + ";}"; //NOI18N |
| StringBuilder setterBody = new StringBuilder(); |
| setterBody.append("{");//NOI18N |
| setterBody.append(longName).append(" = ").append(parName).append(";"); //NOI18N |
| setterBody.append("}");//NOI18N |
| |
| Set<Modifier> mods = new HashSet<>(useModifiers); |
| if (staticMod) { |
| mods.add(Modifier.STATIC); |
| } |
| |
| VariableTree fieldTree = (VariableTree) workingCopy.getTrees().getTree(field); |
| MethodTree[] result = new MethodTree[2]; |
| |
| ExecutableElement getterElm = null; |
| getterElm = EncapsulateFieldRefactoringPlugin.findMethod( |
| workingCopy, |
| (TypeElement) field.getEnclosingElement(), |
| getterName, |
| Collections.<VariableElement>emptyList(), false); |
| if (getterElm == null) { |
| MethodTree getter = make.Method( |
| make.Modifiers(mods), |
| getterName, |
| fieldTree.getType(), |
| Collections.<TypeParameterTree>emptyList(), |
| Collections.<VariableTree>emptyList(), |
| Collections.<ExpressionTree>emptyList(), |
| getterBody, |
| null); |
| result[0] = getter; |
| String jdText = null; |
| String prefix = jdText == null ? "" : jdText + "\n"; // NOI18N |
| Comment comment = Comment.create(Comment.Style.JAVADOC, -2, -2, -2, |
| prefix + "@return the " + field.getSimpleName()); // NOI18N |
| make.addComment(getter, comment, true); |
| } else { |
| elements[0] = getterElm; |
| } |
| |
| ExecutableElement setterElm = null; |
| setterElm = EncapsulateFieldRefactoringPlugin.findMethod( |
| workingCopy, |
| (TypeElement) field.getEnclosingElement(), |
| setterName, |
| Collections.<VariableElement>singletonList(field), false); |
| if (setterElm == null) { |
| VariableTree paramTree = make.Variable( |
| make.Modifiers(Collections.<Modifier>emptySet()), parName, fieldTree.getType(), null); |
| MethodTree setter = make.Method( |
| make.Modifiers(mods), |
| setterName, |
| make.PrimitiveType(TypeKind.VOID), |
| Collections.<TypeParameterTree>emptyList(), |
| Collections.singletonList(paramTree), |
| Collections.<ExpressionTree>emptyList(), |
| setterBody.toString(), |
| null); |
| result[1] = setter; |
| |
| String jdText = null; |
| String prefix = jdText == null ? "" : jdText + "\n"; // NOI18N |
| Comment comment = Comment.create( |
| Comment.Style.JAVADOC, -2, -2, -2, |
| prefix + String.format("@param %s the %s to set", parName, fieldName)); // NOI18N |
| make.addComment(setter, comment, true); |
| } else { |
| elements[1] = setterElm; |
| } |
| return result; |
| } |
| |
| private void addMembers(TypeElement source, GeneratorUtilities genUtils, List<Tree> members) throws IllegalStateException { |
| for (ExecutableElement method : ElementFilter.methodsIn(workingCopy.getElements().getAllMembers(source))) { |
| if (!method.getModifiers().contains(Modifier.NATIVE) |
| && method.getModifiers().contains(Modifier.PUBLIC) |
| && !method.getEnclosingElement().equals(workingCopy.getElements().getTypeElement("java.lang.Object"))) { //NOI18N |
| if (!((method.getReturnType().getKind() == TypeKind.BOOLEAN && method.getSimpleName().contentEquals("equals")) || //NOI18N |
| (method.getReturnType().getKind() == TypeKind.INT && method.getSimpleName().contentEquals("hashCode")))) { //NOI18N |
| addMember(source, method, genUtils, members); |
| } |
| } |
| } |
| } |
| |
| private String decapitalize(CharSequence name) { |
| return Character.toLowerCase(name.charAt(0)) + name.subSequence(1, name.length()).toString() + 's'; |
| } |
| |
| private void createWrap(TypeElement source, String fqn, List<Tree> members) throws IllegalStateException { |
| Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL); |
| final String name = decapitalize(source.getSimpleName()); |
| |
| List<VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.QualIdent(source), null)); //NOI18N |
| String bodyText = preparePattern(workingCopy, source.asType(), WRAP_PATTERNS, name, source.getSimpleName().toString(), fqn); //NOI18N |
| SourcePositions[] positions = new SourcePositions[1]; |
| StatementTree body = workingCopy.getTreeUtilities().parseStatement(bodyText, positions); |
| ModifiersTree modifiers = prepareModifiers(workingCopy, mods, make, false); |
| |
| MethodTree wrap = make.Method(modifiers, "wrap", make.ArrayType(make.QualIdent(fqn)), Collections.<TypeParameterTree>emptyList(), params, Collections.<ExpressionTree>emptyList(), (BlockTree) body, null, true); //NOI18N |
| members.add(wrap); |
| } |
| |
| /* |
| LocalFieldExtension[] result = new LocalFieldExtension[fields.length]; |
| for (int i = 0; i < fields.length; i++) { |
| result[i] = new LocalFieldExtension(fields[i]); |
| } |
| return result; |
| */ |
| |
| /* |
| public static LocalFieldExtension[] wrap(Field[] fields) { |
| return Arrays.stream(fields).map((Field t) -> new LocalFieldExtension(t)).toArray(LocalFieldExtension[]::new); |
| } |
| */ |
| |
| private void createEquals(TypeElement source, GeneratorUtilities genUtils, List<Tree> members) throws IllegalStateException { |
| List<Tree> newTypeParams = new LinkedList<>(); |
| for (TypeParameterElement typeParam : source.getTypeParameters()) { |
| newTypeParams.add(make.Type(typeParam.asType())); |
| } |
| |
| TypeMirror sourceType = source.asType(); |
| Tree newTypeTree = make.Type(refactoring.getNewName()); |
| Tree newSimpleTypeTree = make.Type(refactoring.getNewName()); |
| if (sourceType.getKind() == TypeKind.DECLARED) { |
| if (!newTypeParams.isEmpty()) { |
| newTypeTree = (ExpressionTree) make.ParameterizedType(newTypeTree, newTypeParams); |
| } |
| } |
| |
| Equality equality = refactoring.getEquality(); |
| switch (equality) { |
| case SEPARATE: { |
| /* |
| * boolean equals(Object o) { |
| * return this.delegate.equals(o); |
| * } |
| * |
| * boolean equalsSOURCE(THIS o) { |
| * return this.delegate.equals(o.delegate); |
| * } |
| * |
| * int hashCode() { |
| * return this.delegate.hashCode(); |
| * } |
| */ |
| BlockTree body = make.Block(Collections.singletonList(make.Return(make.MethodInvocation(Collections.EMPTY_LIST, make.MemberSelect(make.Identifier("this.delegate"), "equals"), Collections.singletonList(make.MemberSelect(make.Identifier("o"), DELEGATE))))), false); //NOI18N |
| MethodTree method = make.Method(make.Modifiers(EnumSet.of(Modifier.PUBLIC)), |
| "equals" + refactoring.getNewName(), //NOI18N |
| make.PrimitiveType(TypeKind.BOOLEAN), |
| Collections.EMPTY_LIST, |
| Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "o", newTypeTree, null)), //NOI18N |
| Collections.EMPTY_LIST, |
| body, |
| null, |
| false); |
| members.add(method); |
| } |
| case DELEGATE: { |
| /* |
| * boolean equals(Object o) { |
| * Object target = o; |
| * if(o instanceof THIS) { |
| * target = ((THIS)o).delegate; |
| * } |
| * return this.delegate.equals(target); |
| * } |
| * |
| * int hashCode() { |
| * return this.delegate.hashCode(); |
| * } |
| */ |
| List<StatementTree> statements = new LinkedList<>(); |
| statements.add(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "target", make.Type("Object"), make.Identifier("o"))); |
| statements.add( |
| make.If(make.InstanceOf(make.Identifier("o"), newSimpleTypeTree), make.Block(Collections.singletonList(make.ExpressionStatement( |
| make.Assignment(make.Identifier("target"), |
| make.MemberSelect(make.Parenthesized(make.TypeCast(newSimpleTypeTree, make.Identifier("o"))), DELEGATE)))), false), null)); |
| statements.add(make.Return(make.MethodInvocation(Collections.EMPTY_LIST, make.Identifier("this.delegate.equals"), Collections.singletonList(make.Identifier("target"))))); |
| BlockTree body = make.Block(statements, false); |
| MethodTree method = make.Method(make.Modifiers(EnumSet.of(Modifier.PUBLIC)), |
| "equals", |
| make.PrimitiveType(TypeKind.BOOLEAN), |
| Collections.EMPTY_LIST, |
| Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "o", make.Type("Object"), null)), |
| Collections.EMPTY_LIST, |
| body, |
| null, |
| false); |
| members.add(method); |
| |
| body = make.Block(Collections.singletonList(make.Return(make.MethodInvocation(Collections.EMPTY_LIST, make.MemberSelect(make.Identifier("this.delegate"), "hashCode"), Collections.EMPTY_LIST))), false); //NOI18N |
| method = make.Method(make.Modifiers(EnumSet.of(Modifier.PUBLIC)), |
| "hashCode", //NOI18N |
| make.PrimitiveType(TypeKind.INT), |
| Collections.EMPTY_LIST, |
| Collections.EMPTY_LIST, |
| Collections.EMPTY_LIST, |
| body, |
| null, |
| false); |
| members.add(method); |
| break; |
| } |
| case GENERATE: { |
| MethodTree equalsMethod = createEqualsMethod(workingCopy, (DeclaredType) sourceType, newTypeTree); |
| MethodTree hashMethod = createHashCodeMethod(workingCopy, (DeclaredType) sourceType); |
| members.add(equalsMethod); |
| members.add(hashMethod); |
| break; |
| } |
| } |
| } |
| |
| private void addMember(TypeElement source, ExecutableElement member, GeneratorUtilities genUtils, List<Tree> members) throws IllegalStateException { |
| List<ExpressionTree> paramList = new ArrayList<>(); |
| ExecutableType method = (ExecutableType) workingCopy.getTypes().asMemberOf((DeclaredType) source.asType(), member); |
| final List<? extends VariableElement> parameters = member.getParameters(); |
| final List<? extends TypeMirror> parameterTypes = method.getParameterTypes(); |
| for (int i = 0;i < parameterTypes.size(); i++) { |
| TypeMirror variableElement = parameterTypes.get(i); |
| final ExpressionTree identifier; |
| if (workingCopy.getTypes().isSameType(variableElement, source.asType())) { |
| identifier = make.MemberSelect(make.Identifier(parameters.get(i).getSimpleName()), "delegate"); //NOI18N |
| } else { |
| identifier = make.Identifier(parameters.get(i).getSimpleName()); |
| } |
| paramList.add(identifier); |
| } |
| List<ExpressionTree> typeArguments = new ArrayList<>(); |
| final List<? extends TypeParameterElement> typeParameters = member.getTypeParameters(); |
| for (TypeParameterElement typeParameterElement : typeParameters) { |
| if(typeParameterElement.getEnclosingElement().equals(member)) { |
| IdentifierTree identifier = make.Identifier(typeParameterElement); |
| typeArguments.add(identifier); |
| } |
| } |
| MemberSelectTree memberSelect; |
| final Set<Modifier> mods = EnumSet.copyOf(member.getModifiers()); |
| mods.remove(Modifier.ABSTRACT); |
| mods.remove(Modifier.DEFAULT); |
| if (mods.contains(Modifier.STATIC)) { |
| memberSelect = make.MemberSelect(make.QualIdent(source), member); |
| } else { |
| memberSelect = make.MemberSelect(make.Identifier("delegate"), member); //NOI18N |
| } |
| ExpressionTree methodInvocation = make.MethodInvocation(typeArguments, |
| memberSelect, |
| paramList); |
| TypeMirror methodReturnTypeMirror = method.getReturnType(); |
| Tree methodReturnType = make.Type(method.getReturnType()); |
| boolean hasReturn = true; |
| Types types = workingCopy.getTypes(); |
| if (types.isSameType(methodReturnTypeMirror, types.getNoType(TypeKind.VOID))) { |
| hasReturn = false; |
| } |
| StatementTree statement; |
| if (hasReturn) { |
| if (workingCopy.getTypes().isSameType(methodReturnTypeMirror, source.asType())) { |
| ExpressionTree ident; |
| |
| DeclaredType declaredType = (DeclaredType) methodReturnTypeMirror; |
| List<? extends TypeMirror> returntypeArguments = declaredType.getTypeArguments(); |
| |
| List<ExpressionTree> returntypes = new LinkedList<>(); |
| for (TypeMirror typeMirror : returntypeArguments) { |
| returntypes.add((ExpressionTree) make.Type(typeMirror)); |
| } |
| ident = make.QualIdent(fqn); |
| if (!returntypes.isEmpty()) { |
| ident = (ExpressionTree) make.ParameterizedType(ident, returntypes); |
| } |
| methodReturnType = ident; |
| |
| methodInvocation = make.NewClass(null, Collections.EMPTY_LIST, ident, Collections.singletonList(methodInvocation), null); |
| } |
| statement = make.Return(methodInvocation); |
| } else { |
| statement = make.ExpressionStatement(methodInvocation); |
| } |
| |
| List<AnnotationTree> annotations = member.getEnclosingElement().getKind() == ElementKind.INTERFACE? |
| Collections.singletonList(make.Annotation(make.Type("Override"), |
| Collections.EMPTY_LIST)): |
| Collections.<AnnotationTree>emptyList(); |
| ModifiersTree modifiers = make.Modifiers(mods, annotations); |
| |
| List<TypeParameterTree> newTypeParams = new ArrayList<>(member.getTypeParameters().size()); |
| transformTypeParameters(member, member.getTypeParameters(), make, genUtils, newTypeParams); |
| |
| List<VariableTree> newParameters = new ArrayList<>(parameterTypes.size()); |
| for (int i = 0; i < parameterTypes.size(); i++) { |
| TypeMirror variableType = parameterTypes.get(i); |
| VariableElement variableElement = parameters.get(i); |
| if (workingCopy.getTypes().isSameType(variableType, source.asType())) { |
| Tree ident = make.QualIdent(fqn); |
| if (variableType.getKind() == TypeKind.DECLARED) { |
| DeclaredType declaredType = (DeclaredType) variableType; |
| List<? extends TypeMirror> arguments = declaredType.getTypeArguments(); |
| List<Tree> newArguments = new ArrayList<>(arguments.size()); |
| for (TypeMirror typeMirror : arguments) { |
| newArguments.add(make.Type(typeMirror)); |
| } |
| if (!newArguments.isEmpty()) { |
| ident = (ExpressionTree) make.ParameterizedType(ident, newArguments); |
| } |
| } |
| newParameters.add(make.Variable(make.Modifiers(variableElement.getModifiers()), variableElement.getSimpleName(), ident, null)); |
| } else { |
| newParameters.add(make.Variable(make.Modifiers(variableElement.getModifiers()), variableElement.getSimpleName(), make.Type(variableType), null)); |
| } |
| } |
| |
| final List<? extends TypeMirror> thrownTypes = method.getThrownTypes(); |
| List<ExpressionTree> newThrownTypes = new ArrayList<>(thrownTypes.size()); |
| for (TypeMirror typeMirror : thrownTypes) { |
| newThrownTypes.add((ExpressionTree) make.Type(typeMirror)); |
| } |
| |
| MethodTree newMethod = make.Method(modifiers, member.getSimpleName(), methodReturnType, newTypeParams, newParameters, newThrownTypes, make.Block(Collections.singletonList(statement), false), null, member.isVarArgs()); |
| newMethod = genUtils.importFQNs(newMethod); |
| DocCommentTree javadoc = workingCopy.getDocTrees().getDocCommentTree(member); |
| if (javadoc != null && !javadoc.getFullBody().isEmpty()) { |
| Comment comment = Comment.create(Comment.Style.JAVADOC, javadoc.toString()); |
| make.addComment(newMethod, comment, true); |
| } |
| members.add(newMethod); |
| } |
| |
| private List<Tree> addInterfaces(TypeElement source) { |
| List<Tree> implementsList = new ArrayList<>(); |
| Set<String> implemented = new HashSet<>(); |
| TypeElement typeElement = source; |
| while (typeElement != null) { |
| for (TypeMirror typeMirror : typeElement.getInterfaces()) { |
| Tree type = make.Type(typeMirror); |
| if (typeMirror.getKind() == TypeKind.DECLARED) { |
| DeclaredType declaredType = (DeclaredType) typeMirror; |
| List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); |
| List<Tree> newTypeParams = new LinkedList<>(); |
| for (TypeMirror typeParam : typeArguments) { |
| Element element = workingCopy.getTypes().asElement(typeParam); |
| if (source.equals(element)) { |
| newTypeParams.add(make.QualIdent(fqn)); |
| } else { |
| newTypeParams.add(make.Type(typeParam)); |
| } |
| } |
| if (!newTypeParams.isEmpty()) { |
| type = make.ParameterizedType(make.QualIdent(declaredType.asElement()), newTypeParams); |
| } |
| } |
| if (implemented.add(typeMirror.toString())) { |
| implementsList.add(type); |
| } |
| } |
| TypeMirror superclass = typeElement.getSuperclass(); |
| if (superclass.getKind() != TypeKind.NONE) { |
| typeElement = (TypeElement) workingCopy.getTypes().asElement(superclass); |
| } else { |
| typeElement = null; |
| } |
| } |
| return implementsList; |
| } |
| |
| private void transformTypeParameters(Element source, List<? extends TypeParameterElement> typeParams, TreeMaker make, GeneratorUtilities genUtils, List<TypeParameterTree> newTypeParams) { |
| for (TypeParameterElement typeParam : typeParams) { |
| if(typeParam.getEnclosingElement().equals(source)) { |
| List<? extends TypeMirror> bounds = typeParam.getBounds(); |
| List<ExpressionTree> newBounds = new ArrayList<>(bounds.size()); |
| for (TypeMirror typeMirror : bounds) { |
| TypeMirror typeObject = workingCopy.getElements().getTypeElement("java.lang.Object").asType(); |
| if (!workingCopy.getTypes().isSameType(typeMirror, typeObject)) { |
| ExpressionTree type = (ExpressionTree) make.Type(typeMirror); |
| newBounds.add(type); |
| } |
| } |
| TypeParameterTree typeParameterTree = make.TypeParameter(typeParam.getSimpleName(), newBounds); |
| if (!typeParameterTree.getBounds().isEmpty()) { |
| typeParameterTree = (TypeParameterTree) genUtils.importFQNs(typeParameterTree); |
| } |
| newTypeParams.add(typeParameterTree); |
| } |
| } |
| } |
| |
| private void addConstructors(final TypeElement origClass, List<Tree> members, Modifier needed) { |
| final GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy); |
| |
| Tree type = make.Type(origClass.asType()); |
| VariableTree parameter = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "delegate", type, null); |
| |
| EnumSet<Modifier> modifiers = origClass.getModifiers().isEmpty()? EnumSet.noneOf(Modifier.class) : EnumSet.copyOf(origClass.getModifiers()); |
| modifiers.remove(Modifier.STATIC); |
| modifiers.remove(Modifier.FINAL); |
| modifiers.remove(Modifier.ABSTRACT); |
| |
| if (refactoring.getWrap() && origClass.getKind() != ElementKind.INTERFACE) { |
| // create constructor |
| AssignmentTree assignment = make.Assignment(make.MemberSelect(make.Identifier("this"), "delegate"), make.Identifier("delegate")); //NOI18N |
| ExpressionStatementTree statement = make.ExpressionStatement(assignment); |
| BlockTree block = make.Block(Collections.singletonList(statement), false); |
| MethodTree newConstr = make.Method(make.Modifiers(modifiers), |
| refactoring.getNewName(), |
| null, |
| Collections.EMPTY_LIST, |
| Collections.singletonList(parameter), |
| Collections.EMPTY_LIST, |
| block, |
| null); |
| |
| newConstr = genUtils.importFQNs(newConstr); |
| members.add(newConstr); |
| } |
| final boolean isAbstract = origClass.getModifiers().contains(Modifier.ABSTRACT); |
| if (!isAbstract) { |
| for (ExecutableElement constr : ElementFilter.constructorsIn(origClass.getEnclosedElements())) { |
| Set<Modifier> cMods = constr.getModifiers(); |
| if(!cMods.contains(Modifier.PUBLIC)) { |
| if(cMods.contains(Modifier.PRIVATE)) continue; |
| if(needed != null) { |
| if(!cMods.contains(needed)) { |
| continue; |
| } |
| } |
| } |
| final List<? extends TypeParameterElement> typeParameters = constr.getTypeParameters(); |
| List<TypeParameterTree> newTypeParams = new ArrayList<>(typeParameters.size()); |
| transformTypeParameters(constr, typeParameters, make, genUtils, newTypeParams); |
| |
| List<ExpressionTree> newTypeArguments = new ArrayList<>(); |
| for (TypeParameterElement typeParameterElement : typeParameters) { |
| IdentifierTree identifier = make.Identifier(typeParameterElement); |
| newTypeArguments.add(identifier); |
| } |
| |
| List<ExpressionTree> newArguments = new ArrayList<>(); |
| for (VariableElement variableElement : constr.getParameters()) { |
| IdentifierTree identifier = make.Identifier(variableElement); |
| newArguments.add(identifier); |
| } |
| |
| List<? extends VariableElement> parameters = constr.getParameters(); |
| List<VariableTree> newParams = new ArrayList<>(typeParameters.size()); |
| for (VariableElement variableElement : parameters) { |
| VariableTree var = make.Variable(variableElement, null); |
| newParams.add(var); |
| } |
| List<? extends TypeMirror> thrownTypes = constr.getThrownTypes(); |
| List<ExpressionTree> newThrownTypes = new ArrayList<>(thrownTypes.size()); |
| for (TypeMirror typeMirror : thrownTypes) { |
| Tree thrownType = make.Type(typeMirror); |
| newThrownTypes.add((ExpressionTree) thrownType); |
| } |
| |
| ExpressionTree expression; |
| if (refactoring.getWrap()) { |
| ExpressionTree newClassTree = make.NewClass(null, newTypeArguments, (ExpressionTree)type, newArguments, null); |
| expression = make.Assignment(make.MemberSelect(make.Identifier("this"), "delegate"), newClassTree); //NOI18N |
| } else { |
| expression = make.MethodInvocation(newTypeArguments, make.Identifier("super"), newArguments); //NOI18N |
| } |
| |
| ExpressionStatementTree statement = make.ExpressionStatement(expression); |
| BlockTree block = make.Block(Collections.singletonList(statement), false); |
| |
| // create constructor |
| MethodTree newConstr = make.Method(make.Modifiers(modifiers), |
| refactoring.getNewName(), |
| null, |
| newTypeParams, |
| newParams, |
| newThrownTypes, block, null); |
| |
| newConstr = genUtils.importFQNs(newConstr); |
| DocCommentTree javadoc = workingCopy.getDocTrees().getDocCommentTree(constr); |
| if (javadoc != null && !javadoc.getFullBody().isEmpty()) { |
| Comment comment = Comment.create(Comment.Style.JAVADOC, javadoc.toString()); |
| make.addComment(newConstr, comment, true); |
| } |
| members.add(newConstr); |
| } |
| } |
| } |
| |
| @Override |
| public Tree visitClass(ClassTree node, Element p) { |
| Element element = workingCopy.getTrees().getElement(getCurrentPath()); |
| if (p.equals(element)) { |
| return node; |
| } |
| return super.visitClass(node, p); |
| } |
| |
| @Override |
| public Tree visitIdentifier(IdentifierTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitIdentifier(node, p); |
| } |
| TreePath typePath = new TreePath(getCurrentPath(), node); |
| rewriteType(typePath, p, node); |
| return super.visitIdentifier(node, p); |
| } |
| |
| // @Override |
| // public Tree visitVariable(VariableTree node, Element p) { |
| // if (!refactoring.getReplace()) { |
| // return super.visitVariable(node, p); |
| // } |
| // TreePath currentPath = getCurrentPath(); |
| // Tree typeTree = node.getType(); |
| // TreePath typePath = new TreePath(currentPath, typeTree); |
| // rewriteType(typePath, p, typeTree); |
| // return super.visitVariable(node, p); |
| // } |
| // @Override |
| // public Tree visitNewClass(NewClassTree node, Element p) { |
| // if (!refactoring.getReplace()) { |
| // return super.visitNewClass(node, p); |
| // } |
| // TreePath currentPath = getCurrentPath(); |
| // ExpressionTree typeTree = node.getIdentifier(); |
| // TreePath typePath = new TreePath(currentPath, typeTree); |
| // rewriteType(typePath, p, typeTree); |
| // return super.visitNewClass(node, p); |
| // } |
| // @Override |
| // public Tree visitTypeCast(TypeCastTree node, Element p) { |
| // if (!refactoring.getReplace()) { |
| // return super.visitTypeCast(node, p); |
| // } |
| // TreePath currentPath = getCurrentPath(); |
| // Tree typeTree = node.getType(); |
| // TreePath typePath = new TreePath(currentPath, typeTree); |
| // rewriteType(typePath, p, typeTree); |
| // return super.visitTypeCast(node, p); |
| // } |
| // @Override |
| // public Tree visitMethod(MethodTree node, Element p) { |
| // if (!refactoring.getReplace()) { |
| // return super.visitMethod(node, p); |
| // } |
| // TreePath currentPath = getCurrentPath(); |
| // ExecutableElement method = (ExecutableElement) workingCopy.getTrees().getElement(currentPath); |
| // TypeMirror returnType = method.getReturnType(); |
| // Element element = workingCopy.getTypes().asElement(returnType); |
| // if (p.equals(element)) { |
| // Tree typeTree = node.getReturnType(); |
| // ExpressionTree ident = make.QualIdent(fqn); |
| // if (typeTree.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { |
| // List<? extends Tree> typeArguments = ((ParameterizedTypeTree) typeTree).getTypeArguments(); |
| // if (!typeArguments.isEmpty()) { |
| // ident = (ExpressionTree) make.ParameterizedType(ident, typeArguments); |
| // } |
| // } |
| // rewrite(typeTree, ident); |
| // } |
| // return super.visitMethod(node, p); |
| // } |
| @Override |
| public Tree visitMethodInvocation(MethodInvocationTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitMethodInvocation(node, p); |
| } |
| TreePath currentPath = getCurrentPath(); |
| TypeMirror returnType = workingCopy.getTrees().getTypeMirror(currentPath); |
| boolean fromArray = false; |
| if (returnType != null && returnType.getKind() == TypeKind.ARRAY) { |
| ArrayType array = (ArrayType) returnType; |
| returnType = array.getComponentType(); |
| fromArray = true; |
| } |
| Element typeElement = workingCopy.getTypes().asElement(returnType); |
| |
| // TODO: Check if the method is in the scope, then skip it. |
| if (p.equals(typeElement)) { |
| if(willBeExtended(currentPath, node, returnType)) { |
| return super.visitMethodInvocation(node, p); |
| } |
| ExpressionTree ident; |
| |
| DeclaredType declaredType = (DeclaredType) returnType; |
| List<? extends TypeMirror> typeArguments = declaredType.getTypeArguments(); |
| |
| List<ExpressionTree> types = new LinkedList<>(); |
| for (TypeMirror typeMirror : typeArguments) { |
| types.add((ExpressionTree) make.Type(typeMirror)); |
| } |
| ident = make.QualIdent(fqn); |
| if (!types.isEmpty()) { |
| ident = (ExpressionTree) make.ParameterizedType(ident, types); |
| } |
| |
| Tree newClass; |
| if(fromArray) { |
| newClass = make.MethodInvocation(Collections.EMPTY_LIST, make.MemberSelect(ident, "wrap"), Collections.singletonList(node)); |
| } else { |
| newClass = make.NewClass(null, Collections.EMPTY_LIST, ident, Collections.singletonList(node), null); |
| } |
| rewrite(node, newClass); |
| } |
| |
| return super.visitMethodInvocation(node, p); |
| } |
| |
| private boolean willBeExtended(TreePath currentPath, MethodInvocationTree node, TypeMirror returnType) { |
| boolean skip = true; |
| ExpressionTree methodSelect = node.getMethodSelect(); |
| if (methodSelect.getKind() == Tree.Kind.MEMBER_SELECT) { |
| MemberSelectTree selectTree = (MemberSelectTree) methodSelect; |
| Element el = workingCopy.getTrees().getElement(new TreePath(currentPath, selectTree)); |
| ElementHandle handle = el == null ? null : ElementHandle.create(el); |
| if(handle != null && JavaRefactoringUtils.isFromLibrary(handle, workingCopy.getClasspathInfo())) { |
| skip = false; |
| } |
| } |
| return skip; |
| } |
| |
| private void rewriteType(TreePath typePath, Element p, Tree typeTree) { |
| Element element = workingCopy.getTrees().getElement(typePath); |
| if (p.equals(element)) { |
| ExpressionTree ident = make.QualIdent(fqn); |
| if (typeTree.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { |
| List<? extends Tree> typeArguments = ((ParameterizedTypeTree) typeTree).getTypeArguments(); |
| if (!typeArguments.isEmpty()) { |
| ident = (ExpressionTree) make.ParameterizedType(ident, typeArguments); |
| } |
| } |
| rewrite(typeTree, ident); |
| } |
| } |
| |
| @Override |
| public Tree visitAssignment(AssignmentTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitAssignment(node, p); |
| } |
| ExpressionTree variable = node.getVariable(); |
| boolean isArray = false; |
| while (variable.getKind() == Tree.Kind.ARRAY_ACCESS) { |
| isArray = true; |
| // int[] a; a[a[0]][a[1]] = 0; // scan also array indices |
| scan(((ArrayAccessTree) variable).getIndex(), p); |
| variable = ((ArrayAccessTree) variable).getExpression(); |
| } |
| |
| Element el = workingCopy.getTrees().getElement(new TreePath(getCurrentPath(), variable)); |
| if(el != null) { |
| Element enclosingElement = el.getEnclosingElement(); |
| if (enclosingElement != null && enclosingElement.equals(p) |
| && (isArray || checkAssignmentInsideExpression())) { |
| ElementHandle<Element> handle = ElementHandle.create(el); |
| String[] getterSetter = getterSetterMap.get(handle); // TODO: Check for null and solve ! |
| if (getterSetter != null) { |
| if (isArray) { |
| ExpressionTree invkgetter = createGetterInvokation(variable, getterSetter[0]); |
| rewrite(variable, invkgetter); |
| } else { |
| ExpressionTree setter = createMemberSelection(variable, getterSetter[1]); |
| |
| // resolve types |
| Trees trees = workingCopy.getTrees(); |
| ExpressionTree expTree = node.getExpression(); |
| ExpressionTree newExpTree; |
| TreePath varPath = trees.getPath(workingCopy.getCompilationUnit(), variable); |
| TreePath expPath = trees.getPath(workingCopy.getCompilationUnit(), expTree); |
| TypeMirror varType = trees.getTypeMirror(varPath); |
| TypeMirror expType = trees.getTypeMirror(expPath); |
| if (workingCopy.getTypes().isSubtype(expType, varType)) { |
| newExpTree = expTree; |
| } else { |
| newExpTree = make.TypeCast(make.Type(varType), expTree); |
| } |
| |
| MethodInvocationTree invksetter = make.MethodInvocation( |
| Collections.<ExpressionTree>emptyList(), |
| setter, |
| Collections.singletonList(newExpTree)); |
| |
| rewrite(node, invksetter); |
| } |
| } |
| } |
| } |
| return scan(node.getExpression(), p); |
| } |
| |
| @Override |
| public Tree visitCompoundAssignment(CompoundAssignmentTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitCompoundAssignment(node, p); |
| } |
| ExpressionTree variable = node.getVariable(); |
| boolean isArray = false; |
| while (variable.getKind() == Tree.Kind.ARRAY_ACCESS) { |
| isArray = true; |
| variable = ((ArrayAccessTree) variable).getExpression(); |
| } |
| |
| Element el = workingCopy.getTrees().getElement(new TreePath(getCurrentPath(), variable)); |
| if(el != null) { |
| Element enclosingElement = el.getEnclosingElement(); |
| if (enclosingElement != null && enclosingElement.equals(p) |
| // check (field += 3) == 3 |
| && (isArray || checkAssignmentInsideExpression())) { |
| ElementHandle<Element> handle = ElementHandle.create(el); |
| String[] getterSetter = getterSetterMap.get(handle); // TODO: Check for null and solve ! |
| if (getterSetter != null) { |
| if (isArray) { |
| ExpressionTree invkgetter = createGetterInvokation(variable, getterSetter[0]); |
| rewrite(variable, invkgetter); |
| } else { |
| ExpressionTree setter = createMemberSelection(variable, getterSetter[1]); |
| |
| // translate compound op to binary op; ADD_ASSIGNMENT -> ADD |
| String s = node.getKind().name(); |
| s = s.substring(0, s.length() - "_ASSIGNMENT".length()); // NOI18N |
| Tree.Kind operator = Tree.Kind.valueOf(s); |
| |
| ExpressionTree invkgetter = createGetterInvokation(variable, getterSetter[0]); |
| |
| // resolve types |
| Trees trees = workingCopy.getTrees(); |
| ExpressionTree expTree = node.getExpression(); |
| ExpressionTree newExpTree; |
| TreePath varPath = trees.getPath(workingCopy.getCompilationUnit(), variable); |
| TreePath expPath = trees.getPath(workingCopy.getCompilationUnit(), expTree); |
| TypeMirror varType = trees.getTypeMirror(varPath); |
| // getter need not exist yet, use variable to resolve type of binary expression |
| ExpressionTree expTreeFake = make.Binary(operator, variable, expTree); |
| TypeMirror expType = workingCopy.getTreeUtilities().attributeTree(expTreeFake, trees.getScope(expPath)); |
| |
| newExpTree = make.Binary(operator, invkgetter, expTree); |
| if (!workingCopy.getTypes().isSubtype(expType, varType)) { |
| newExpTree = make.TypeCast(make.Type(varType), make.Parenthesized(newExpTree)); |
| } |
| |
| MethodInvocationTree invksetter = make.MethodInvocation( |
| Collections.<ExpressionTree>emptyList(), |
| setter, |
| Collections.singletonList(newExpTree)); |
| rewrite(node, invksetter); |
| } |
| } |
| } |
| } |
| return scan(node.getExpression(), p); |
| } |
| |
| @Override |
| public Tree visitUnary(UnaryTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitUnary(node, p); |
| } |
| ExpressionTree t = node.getExpression(); |
| Tree.Kind kind = node.getKind(); |
| boolean isArrayOrImmutable = kind != Tree.Kind.POSTFIX_DECREMENT |
| && kind != Tree.Kind.POSTFIX_INCREMENT |
| && kind != Tree.Kind.PREFIX_DECREMENT |
| && kind != Tree.Kind.PREFIX_INCREMENT; |
| while (t.getKind() == Tree.Kind.ARRAY_ACCESS) { |
| isArrayOrImmutable = true; |
| t = ((ArrayAccessTree) t).getExpression(); |
| } |
| Element el = workingCopy.getTrees().getElement(new TreePath(getCurrentPath(), t)); |
| if (el != null) { |
| Element enclosingElement = el.getEnclosingElement(); |
| if (enclosingElement != null && enclosingElement.equals(p) |
| && (isArrayOrImmutable || checkAssignmentInsideExpression())) { |
| ElementHandle<Element> handle = ElementHandle.create(el); |
| String[] getterSetter = getterSetterMap.get(handle); // TODO: Check for null and solve ! |
| // check (++field + 3) |
| if (getterSetter != null) { |
| ExpressionTree invkgetter = createGetterInvokation(t, getterSetter[0]); |
| if (isArrayOrImmutable) { |
| rewrite(t, invkgetter); |
| } else { |
| ExpressionTree setter = createMemberSelection(node.getExpression(), getterSetter[1]); |
| |
| Tree.Kind operator = kind == Tree.Kind.POSTFIX_INCREMENT || kind == Tree.Kind.PREFIX_INCREMENT |
| ? Tree.Kind.PLUS |
| : Tree.Kind.MINUS; |
| |
| // resolve types |
| Trees trees = workingCopy.getTrees(); |
| ExpressionTree expTree = node.getExpression(); |
| TreePath varPath = trees.getPath(workingCopy.getCompilationUnit(), expTree); |
| TypeMirror varType = trees.getTypeMirror(varPath); |
| TypeMirror expType = workingCopy.getTypes().getPrimitiveType(TypeKind.INT); |
| ExpressionTree newExpTree = make.Binary(operator, invkgetter, make.Literal(1)); |
| if (!workingCopy.getTypes().isSubtype(expType, varType)) { |
| newExpTree = make.TypeCast(make.Type(varType), make.Parenthesized(newExpTree)); |
| } |
| |
| MethodInvocationTree invksetter = make.MethodInvocation( |
| Collections.<ExpressionTree>emptyList(), |
| setter, |
| Collections.singletonList(newExpTree)); |
| rewrite(node, invksetter); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public Tree visitMemberSelect(MemberSelectTree node, Element p) { |
| if (!refactoring.getReplace()) { |
| return super.visitMemberSelect(node, p); |
| } |
| Element el = workingCopy.getTrees().getElement(getCurrentPath()); |
| if (el != null) { |
| Element enclosingElement = el.getEnclosingElement(); |
| if (enclosingElement != null && enclosingElement.equals(p)) { |
| ElementHandle<Element> handle = ElementHandle.create(el); |
| String[] getterSetter = getterSetterMap.get(handle); // TODO: Check for null and solve ! |
| if (getterSetter != null) { |
| ExpressionTree nodeNew = createGetterInvokation(node, getterSetter[0]); |
| rewrite(node, nodeNew); |
| } |
| } |
| } |
| return super.visitMemberSelect(node, p); |
| } |
| |
| private boolean checkAssignmentInsideExpression() { |
| Tree exp1 = getCurrentPath().getLeaf(); |
| Tree parent = getCurrentPath().getParentPath().getLeaf(); |
| if (parent.getKind() != Tree.Kind.EXPRESSION_STATEMENT) { |
| problem = JavaPluginUtils.chainProblems(problem, new Problem(false, |
| NbBundle.getMessage( |
| EncapsulateFieldRefactoringPlugin.class, |
| "ERR_EncapsulateInsideAssignment", // NOI18N |
| exp1.toString(), |
| parent.toString(), |
| FileUtil.getFileDisplayName(workingCopy.getFileObject())))); |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * replace current expresion with the proper one.<p> c.field -> c.getField() |
| * field -> getField() or copy in case of refactoring.getGetterName() == |
| * null |
| */ |
| private ExpressionTree createGetterInvokation(ExpressionTree current, String getterName) { |
| ExpressionTree getter = createMemberSelection(current, getterName); |
| |
| MethodInvocationTree invkgetter = make.MethodInvocation( |
| Collections.<ExpressionTree>emptyList(), |
| getter, |
| Collections.<ExpressionTree>emptyList()); |
| return invkgetter; |
| } |
| |
| private ExpressionTree createMemberSelection(ExpressionTree node, String name) { |
| ExpressionTree selector; |
| if (node.getKind() == Tree.Kind.MEMBER_SELECT) { |
| MemberSelectTree select = (MemberSelectTree) node; |
| selector = make.MemberSelect(select.getExpression(), name); |
| } else { |
| selector = make.Identifier(name); |
| } |
| return selector; |
| } |
| public static final String CLASS_FIELD_PREFIX = "_"; // NOI18N |
| |
| /** |
| * Removes the class field prefix from the identifer of a field. For |
| * example, if the class field prefix is "_", the identifier "_name" is |
| * stripped to become "name". |
| * |
| * @param identifierString The identifer to strip. |
| * @return The stripped identifier. |
| */ |
| private static String stripPrefix(String identifierString) { |
| String stripped; |
| if (identifierString.startsWith(CLASS_FIELD_PREFIX) && identifierString.length() > 1) { |
| stripped = identifierString.substring(CLASS_FIELD_PREFIX.length()); |
| } else { |
| stripped = identifierString; |
| } |
| return stripped; |
| } |
| |
| private static StringBuilder getCapitalizedName(VariableElement field) { |
| StringBuilder name = new StringBuilder(stripPrefix(field.getSimpleName().toString())); |
| |
| //Beans naming convention, #165241 |
| if (name.length() > 1 && Character.isUpperCase(name.charAt(1))) { |
| return name; |
| } |
| |
| name.setCharAt(0, Character.toUpperCase(name.charAt(0))); |
| return name; |
| } |
| |
| private String trimNewLines(String javadoc) { |
| if (javadoc == null) { |
| return null; |
| } |
| |
| int len = javadoc.length(); |
| int st = 0; |
| int off = 0; /* |
| * avoid getfield opcode |
| */ |
| char[] val = javadoc.toCharArray(); /* |
| * avoid getfield opcode |
| */ |
| |
| while ((st < len) && Character.isWhitespace(val[off + st])/* |
| * && (val[off + st] <= '\n') |
| */) { |
| st++; |
| } |
| while ((st < len) && Character.isWhitespace(val[off + len - 1])/* |
| * val[off + len - 1] <= '\n') |
| */) { |
| len--; |
| } |
| return ((st > 0) || (len < val.length)) ? javadoc.substring(st, len) : javadoc; |
| } |
| |
| Problem getProblem() { |
| return problem; |
| } |
| |
| //<editor-fold defaultstate="collapsed" desc="Copied from Java Editor CodeGen"> |
| private enum KindOfType { |
| |
| BOOLEAN, |
| BYTE, |
| SHORT, |
| INT, |
| LONG, |
| CHAR, |
| FLOAT, |
| DOUBLE, |
| ENUM, |
| ARRAY_PRIMITIVE, |
| ARRAY, |
| STRING, |
| OTHER; |
| } |
| private static final Map<Acceptor, String> WRAP_PATTERNS; |
| private static final Map<Acceptor, String> EQUALS_PATTERNS; |
| private static final Map<Acceptor, String> HASH_CODE_PATTERNS; |
| |
| static { |
| WRAP_PATTERNS = new LinkedHashMap<>(); |
| |
| WRAP_PATTERNS.put(new SourceVersionAcceptor(SourceVersion.RELEASE_8), "{ return Arrays.stream({VAR0}).map(({VAR1} t) -> new {VAR2}(t)).toArray({VAR2}[]::new); }"); |
| WRAP_PATTERNS.put(new SourceVersionAcceptor(SourceVersion.RELEASE_2), |
| "{ {VAR2}[] result = new {VAR2}[{VAR0}.length];\n" |
| + "for (int i = 0; i < {VAR0}.length; i++) {\n" |
| + " result[i] = new {VAR2}({VAR0}[i]);\n" |
| + "}\n" |
| + "return result; }"); |
| |
| EQUALS_PATTERNS = new LinkedHashMap<>(); |
| |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.BOOLEAN, KindOfType.BYTE, KindOfType.SHORT, KindOfType.INT, KindOfType.LONG, KindOfType.CHAR), "this.{VAR0} != other.{VAR0}"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.FLOAT), "java.lang.Float.floatToIntBits(this.{VAR0}) != java.lang.Float.floatToIntBits(other.{VAR0})"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.DOUBLE), "java.lang.Double.doubleToLongBits(this.{VAR0}) != java.lang.Double.doubleToLongBits(other.{VAR0})"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.ENUM), "this.{VAR0} != other.{VAR0}"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.ARRAY_PRIMITIVE), "! java.util.Arrays.equals(this.{VAR0}, other.{VAR0}"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.ARRAY), "! java.util.Arrays.deepEquals(this.{VAR0}, other.{VAR0}"); |
| EQUALS_PATTERNS.put(new MethodExistsAcceptor("java.util.Objects", "equals", SourceVersion.RELEASE_7), "! java.util.Objects.equals(this.{VAR0}, other.{VAR0})"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.STRING), "(this.{VAR0} == null) ? (other.{VAR0} != null) : !this.{VAR0}.equals(other.{VAR0})"); |
| EQUALS_PATTERNS.put(new SimpleAcceptor(KindOfType.OTHER), "this.{VAR0} != other.{VAR0} && (this.{VAR0} == null || !this.{VAR0}.equals(other.{VAR0}))"); |
| |
| HASH_CODE_PATTERNS = new LinkedHashMap<>(); |
| |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.BYTE, KindOfType.SHORT, KindOfType.INT, KindOfType.CHAR), "this.{VAR0}"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.LONG), "(int) (this.{VAR0} ^ (this.{VAR0} >>> 32))"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.FLOAT), "java.lang.Float.floatToIntBits(this.{VAR0})"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.DOUBLE), "(int) (Double.doubleToLongBits(this.{VAR0}) ^ (Double.doubleToLongBits(this.{VAR0}) >>> 32))"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.BOOLEAN), "(this.{VAR0} ? 1 : 0)"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.ENUM), "(this.{VAR0} != null ? this.{VAR0}.hashCode() : 0)"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.ARRAY_PRIMITIVE), "java.util.Arrays.hashCode(this.{VAR0}"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.ARRAY), "java.util.Arrays.deepHashCode(this.{VAR0}"); |
| HASH_CODE_PATTERNS.put(new MethodExistsAcceptor("java.util.Objects", "hashCode", SourceVersion.RELEASE_7), "java.util.Objects.hashCode(this.{VAR0})"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.STRING), "(this.{VAR0} != null ? this.{VAR0}.hashCode() : 0)"); |
| HASH_CODE_PATTERNS.put(new SimpleAcceptor(KindOfType.OTHER), "(this.{VAR0} != null ? this.{VAR0}.hashCode() : 0)"); |
| } |
| |
| private MethodTree createEqualsMethod(WorkingCopy wc, DeclaredType type, Tree typeTree) { |
| TreeMaker make = wc.getTreeMaker(); |
| Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC); |
| TypeElement objElement = wc.getElements().getTypeElement("java.lang.Object"); //NOI18N |
| List<VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "obj", objElement != null ? make.Type(objElement.asType()) : make.Identifier("Object"), null)); //NOI18N |
| |
| List<StatementTree> statements = new ArrayList<>(); |
| //if (obj == null) return false; |
| statements.add(make.If(make.Binary(Tree.Kind.EQUAL_TO, make.Identifier("obj"), make.Identifier("null")), make.Return(make.Identifier("false")), null)); //NOI18N |
| //if (getClass() != obj.getClass()) return false; |
| statements.add(make.If(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier("getClass"), Collections.<ExpressionTree>emptyList()), //NOI18N |
| make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("obj"), "getClass"), Collections.<ExpressionTree>emptyList())), make.Return(make.Identifier("false")), null)); //NOI18N |
| //<this type> other = (<this type>) o; |
| // if(typeTree) |
| statements.add(make.Variable(make.Modifiers(EnumSet.of(Modifier.FINAL)), "other", typeTree, make.TypeCast(typeTree, make.Identifier("obj")))); //NOI18N |
| |
| ExpressionTree condition = prepareExpression(wc, EQUALS_PATTERNS, type, "delegate"); //NOI18N |
| statements.add(make.If(condition, make.Return(make.Identifier("false")), null)); //NOI18N |
| |
| statements.add(make.Return(make.Identifier("true"))); |
| BlockTree body = make.Block(statements, false); |
| ModifiersTree modifiers = prepareModifiers(wc, mods, make, true); |
| |
| return make.Method(modifiers, "equals", make.PrimitiveType(TypeKind.BOOLEAN), Collections.<TypeParameterTree>emptyList(), params, Collections.<ExpressionTree>emptyList(), body, null); //NOI18N |
| } |
| |
| private MethodTree createHashCodeMethod(WorkingCopy wc, DeclaredType type) { |
| TreeMaker make = wc.getTreeMaker(); |
| Set<Modifier> mods = EnumSet.of(Modifier.PUBLIC); |
| |
| Integer number = refactoring.getContext().lookup(Integer.class); // Used in test code |
| int startNumber; |
| int multiplyNumber; |
| if (number != null) { |
| startNumber = number.intValue(); |
| multiplyNumber = number.intValue(); |
| } else { |
| startNumber = generatePrimeNumber(2, 10); |
| multiplyNumber = generatePrimeNumber(10, 100); |
| } |
| List<StatementTree> statements = new ArrayList<>(); |
| //int hash = <startNumber>; |
| statements.add(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "hash", make.PrimitiveType(TypeKind.INT), make.Literal(startNumber))); //NOI18N |
| |
| TypeMirror tm = type; |
| ExpressionTree variableRead = prepareExpression(wc, HASH_CODE_PATTERNS, tm, "delegate"); //NOI18N |
| statements.add(make.ExpressionStatement(make.Assignment(make.Identifier("hash"), make.Binary(Tree.Kind.PLUS, make.Binary(Tree.Kind.MULTIPLY, make.Literal(multiplyNumber), make.Identifier("hash")), variableRead)))); //NOI18N |
| |
| statements.add(make.Return(make.Identifier("hash"))); //NOI18N |
| BlockTree body = make.Block(statements, false); |
| ModifiersTree modifiers = prepareModifiers(wc, mods, make, true); |
| |
| return make.Method(modifiers, "hashCode", make.PrimitiveType(TypeKind.INT), Collections.<TypeParameterTree>emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>emptyList(), body, null); //NOI18N |
| } |
| |
| private static boolean isPrimeNumber(int n) { |
| int squareRoot = (int) Math.sqrt(n) + 1; |
| if (n % 2 == 0) { |
| return false; |
| } |
| for (int cntr = 3; cntr < squareRoot; cntr++) { |
| if (n % cntr == 0) { |
| return false; |
| } |
| } |
| return true; |
| } |
| static int randomNumber = -1; |
| |
| private static int generatePrimeNumber(int lowerLimit, int higherLimit) { |
| if (randomNumber > 0) { |
| return randomNumber; |
| } |
| |
| Random r = new Random(System.currentTimeMillis()); |
| int proposed = r.nextInt(higherLimit - lowerLimit) + lowerLimit; |
| while (!isPrimeNumber(proposed)) { |
| proposed++; |
| } |
| if (proposed > higherLimit) { |
| proposed--; |
| while (!isPrimeNumber(proposed)) { |
| proposed--; |
| } |
| } |
| return proposed; |
| } |
| |
| private static ModifiersTree prepareModifiers(WorkingCopy wc, Set<Modifier> mods, TreeMaker make, boolean addOverride) { |
| |
| List<AnnotationTree> annotations = new LinkedList<>(); |
| |
| if (addOverride && supportsOverride(wc)) { |
| TypeElement override = wc.getElements().getTypeElement("java.lang.Override"); |
| |
| if (override != null) { |
| annotations.add(wc.getTreeMaker().Annotation(wc.getTreeMaker().QualIdent(override), Collections.<ExpressionTree>emptyList())); |
| } |
| } |
| |
| ModifiersTree modifiers = make.Modifiers(mods, annotations); |
| |
| return modifiers; |
| } |
| |
| /** |
| * @param info tested file's info |
| * |
| * @return true if SourceVersion of source represented by provided info |
| * supports Override |
| */ |
| private static boolean supportsOverride(CompilationInfo info) { |
| return SourceVersion.RELEASE_5.compareTo(info.getSourceVersion()) <= 0 |
| && info.getElements().getTypeElement("java.lang.Override") != null; |
| } |
| |
| private static KindOfType detectKind(CompilationInfo info, TypeMirror tm) { |
| if (tm.getKind().isPrimitive()) { |
| return KindOfType.valueOf(tm.getKind().name()); |
| } |
| |
| if (tm.getKind() == TypeKind.ARRAY) { |
| return ((ArrayType) tm).getComponentType().getKind().isPrimitive() ? KindOfType.ARRAY_PRIMITIVE : KindOfType.ARRAY; |
| } |
| |
| if (tm.getKind() == TypeKind.DECLARED) { |
| Types t = info.getTypes(); |
| TypeElement en = info.getElements().getTypeElement("java.lang.Enum"); |
| |
| if (en != null) { |
| if (t.isSubtype(tm, t.erasure(en.asType()))) { |
| return KindOfType.ENUM; |
| } |
| } |
| |
| if (((DeclaredType) tm).asElement().getKind().isClass() && ((TypeElement) ((DeclaredType) tm).asElement()).getQualifiedName().contentEquals("java.lang.String")) { |
| return KindOfType.STRING; |
| } |
| } |
| |
| return KindOfType.OTHER; |
| } |
| |
| private static String choosePattern(CompilationInfo info, TypeMirror tm, Map<Acceptor, String> patterns) { |
| for (Map.Entry<Acceptor, String> e : patterns.entrySet()) { |
| if (e.getKey().accept(info, tm)) { |
| return e.getValue(); |
| } |
| } |
| |
| throw new IllegalStateException(); |
| } |
| |
| private static ExpressionTree prepareExpression(WorkingCopy wc, Map<Acceptor, String> patterns, TypeMirror tm, String... ve/*, Scope scope*/) { |
| String conditionText = preparePattern(wc, tm, patterns, ve); |
| ExpressionTree exp = wc.getTreeUtilities().parseExpression(conditionText, new SourcePositions[1]); |
| |
| exp = GeneratorUtilities.get(wc).importFQNs(exp); |
| // wc.getTreeUtilities().attributeTree(exp, scope); |
| |
| return exp; |
| } |
| |
| private static String preparePattern(WorkingCopy wc, TypeMirror tm, Map<Acceptor, String> patterns, String... ve) { |
| String pattern = choosePattern(wc, tm, patterns); |
| assert pattern != null; |
| Map<String, String> variables = new HashMap<>(ve.length); |
| for (int i = 0; i < ve.length; i++) { |
| String string = ve[i]; |
| variables.put("VAR"+ i, string); |
| } |
| String conditionText = MapFormat.format(pattern, variables); |
| return conditionText; |
| } |
| |
| private static interface Acceptor { |
| |
| public boolean accept(CompilationInfo info, TypeMirror tm); |
| } |
| |
| private static final class SimpleAcceptor implements Acceptor { |
| |
| private final Set<KindOfType> kinds; |
| |
| public SimpleAcceptor(KindOfType kind) { |
| kinds = EnumSet.of(kind); |
| } |
| |
| public SimpleAcceptor(KindOfType kind, KindOfType... moreKinds) { |
| this.kinds = EnumSet.of(kind); |
| this.kinds.addAll(Arrays.asList(moreKinds)); |
| } |
| |
| public boolean accept(CompilationInfo info, TypeMirror tm) { |
| return kinds.contains(detectKind(info, tm)); |
| } |
| } |
| |
| private static class SourceVersionAcceptor implements Acceptor { |
| |
| private final SourceVersion minimalVersion; |
| |
| public SourceVersionAcceptor(SourceVersion minimalVersion) { |
| this.minimalVersion = minimalVersion; |
| } |
| |
| @Override |
| public boolean accept(CompilationInfo info, TypeMirror tm) { |
| return !(minimalVersion != null && minimalVersion.compareTo(info.getSourceVersion()) > 0); |
| } |
| } |
| |
| private static final class MethodExistsAcceptor extends SourceVersionAcceptor { |
| |
| private final String fqn; |
| private final String methodName; |
| |
| public MethodExistsAcceptor(String fqn, String methodName) { |
| this(fqn, methodName, null); |
| } |
| |
| public MethodExistsAcceptor(String fqn, String methodName, SourceVersion minimalVersion) { |
| super(minimalVersion); |
| this.fqn = fqn; |
| this.methodName = methodName; |
| } |
| |
| @Override |
| public boolean accept(CompilationInfo info, TypeMirror tm) { |
| if (!super.accept(info, tm)) { |
| return false; |
| } |
| |
| TypeElement clazz = info.getElements().getTypeElement(fqn); |
| |
| if (clazz == null) { |
| return false; |
| } |
| |
| for (ExecutableElement m : ElementFilter.methodsIn(clazz.getEnclosedElements())) { |
| if (m.getSimpleName().contentEquals(methodName)) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| } |
| //</editor-fold> |
| } |