1 19 package org.netbeans.modules.java.editor.codegen; 20 21 import com.sun.source.tree.BlockTree; 22 import com.sun.source.tree.ClassTree; 23 import com.sun.source.tree.ExpressionTree; 24 import com.sun.source.tree.MethodTree; 25 import com.sun.source.tree.StatementTree; 26 import com.sun.source.tree.Tree; 27 import com.sun.source.tree.TypeParameterTree; 28 import com.sun.source.tree.VariableTree; 29 import com.sun.source.util.SourcePositions; 30 import com.sun.source.util.TreePath; 31 import java.awt.Dialog ; 32 import java.io.IOException ; 33 import java.util.ArrayList ; 34 import java.util.Collections ; 35 import java.util.EnumSet ; 36 import java.util.List ; 37 import java.util.Random ; 38 import java.util.Set ; 39 import javax.lang.model.element.Element; 40 import javax.lang.model.element.ElementKind; 41 import javax.lang.model.element.Modifier; 42 import javax.lang.model.element.TypeElement; 43 import javax.lang.model.element.VariableElement; 44 import javax.lang.model.type.DeclaredType; 45 import javax.lang.model.type.TypeKind; 46 import javax.lang.model.util.ElementFilter; 47 import javax.swing.text.JTextComponent ; 48 import org.netbeans.api.java.source.CancellableTask; 49 import org.netbeans.api.java.source.CompilationController; 50 import org.netbeans.api.java.source.ElementHandle; 51 import org.netbeans.api.java.source.JavaSource; 52 import org.netbeans.api.java.source.TreeMaker; 53 import org.netbeans.api.java.source.WorkingCopy; 54 import org.netbeans.modules.editor.java.Utilities; 55 import org.netbeans.modules.java.editor.codegen.ui.ElementNode; 56 import org.netbeans.modules.java.editor.codegen.ui.EqualsHashCodePanel; 57 import org.openide.DialogDescriptor; 58 import org.openide.DialogDisplayer; 59 import org.openide.util.Exceptions; 60 import org.openide.util.NbBundle; 61 62 66 public class EqualsHashCodeGenerator implements CodeGenerator { 67 68 public static class Factory implements CodeGenerator.Factory { 69 70 Factory() { 71 } 72 73 public Iterable <? extends CodeGenerator> create(CompilationController controller, TreePath path) throws IOException { 74 path = Utilities.getPathElementOfKind(Tree.Kind.CLASS, path); 75 if (path == null) 76 return Collections.emptySet(); 77 controller.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); 78 TypeElement typeElement = (TypeElement)controller.getTrees().getElement(path); 79 if (typeElement.getKind() != ElementKind.CLASS) 80 return Collections.emptySet(); 81 List <ElementNode.Description> descriptions = new ArrayList <ElementNode.Description>(); 82 for (VariableElement variableElement : ElementFilter.fieldsIn(typeElement.getEnclosedElements())) 83 descriptions.add(ElementNode.Description.create(variableElement, null, true, false)); 84 if (descriptions.isEmpty()) 85 return Collections.emptySet(); 86 return Collections.singleton(new EqualsHashCodeGenerator(ElementNode.Description.create(typeElement, descriptions, false, false))); 87 } 88 } 89 90 ElementNode.Description description; 91 92 93 private EqualsHashCodeGenerator(ElementNode.Description description) { 94 this.description = description; 95 } 96 97 public String getDisplayName() { 98 return org.openide.util.NbBundle.getMessage(EqualsHashCodeGenerator.class, "LBL_equals_and_hashcode"); } 100 101 public void invoke(JTextComponent component) { 102 final EqualsHashCodePanel panel = new EqualsHashCodePanel(description); 103 DialogDescriptor dialogDescriptor = new DialogDescriptor(panel, NbBundle.getMessage(ConstructorGenerator.class, "LBL_generate_equals_and_hashcode")); Dialog dialog = DialogDisplayer.getDefault().createDialog(dialogDescriptor); 105 dialog.setVisible(true); 106 if (dialogDescriptor.getValue() == DialogDescriptor.OK_OPTION) { 107 JavaSource js = JavaSource.forDocument(component.getDocument()); 108 if (js != null) { 109 try { 110 final int caretOffset = component.getCaretPosition(); 111 js.runModificationTask(new CancellableTask<WorkingCopy>() { 112 public void cancel() { 113 } 114 public void run(WorkingCopy copy) throws IOException { 115 copy.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED); 116 TreePath path = copy.getTreeUtilities().pathFor(caretOffset); 117 path = Utilities.getPathElementOfKind(Tree.Kind.CLASS, path); 118 int idx = 0; 119 SourcePositions sourcePositions = copy.getTrees().getSourcePositions(); 120 for (Tree tree : ((ClassTree)path.getLeaf()).getMembers()) { 121 if (sourcePositions.getStartPosition(path.getCompilationUnit(), tree) < caretOffset) 122 idx++; 123 else 124 break; 125 } 126 ArrayList <VariableElement> equalsElements = new ArrayList <VariableElement>(); 127 for (ElementHandle<? extends Element> elementHandle : panel.getEqualsVariables()) 128 equalsElements.add((VariableElement)elementHandle.resolve(copy)); 129 ArrayList <VariableElement> hashCodeElements = new ArrayList <VariableElement>(); 130 for (ElementHandle<? extends Element> elementHandle : panel.getHashCodeVariables()) 131 hashCodeElements.add((VariableElement)elementHandle.resolve(copy)); 132 generateEqualsAndHashCode(copy, path, equalsElements, hashCodeElements, idx); 133 } 134 }).commit(); 135 } catch (IOException ex) { 136 Exceptions.printStackTrace(ex); 137 } 138 } 139 } 140 } 141 142 private static void generateEqualsAndHashCode(WorkingCopy wc, TreePath path, Iterable <? extends VariableElement> equalsFields, Iterable <? extends VariableElement> hashCodeFields, int index) { 143 assert path.getLeaf().getKind() == Tree.Kind.CLASS; 144 TypeElement te = (TypeElement)wc.getTrees().getElement(path); 145 if (te != null) { 146 TreeMaker make = wc.getTreeMaker(); 147 ClassTree nue = (ClassTree)path.getLeaf(); 148 nue = make.insertClassMember(nue, index, createHashCodeMethod(wc, hashCodeFields, (DeclaredType)te.asType())); 149 nue = make.insertClassMember(nue, index, createEqualsMethod(wc, equalsFields, (DeclaredType)te.asType())); 150 wc.rewrite(path.getLeaf(), nue); 151 } 152 } 153 154 private static MethodTree createEqualsMethod(WorkingCopy wc, Iterable <? extends VariableElement> equalsFields, DeclaredType type) { 155 TreeMaker make = wc.getTreeMaker(); 156 Set <Modifier> mods = EnumSet.of(Modifier.PUBLIC); 157 List <VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "obj", make.Type(wc.getElements().getTypeElement("java.lang.Object").asType()), null)); 159 List <StatementTree> statements = new ArrayList <StatementTree>(); 160 statements.add(make.If(make.Binary(Tree.Kind.EQUAL_TO, make.Identifier("obj"), make.Identifier("null")), make.Return(make.Identifier("false")), null)); statements.add(make.If(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier("getClass"), Collections.<ExpressionTree>emptyList()), make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("obj"), "getClass"), Collections.<ExpressionTree>emptyList())), make.Return(make.Identifier("false")), null)); statements.add(make.Variable(make.Modifiers(EnumSet.of(Modifier.FINAL)), "other", make.Type(type), make.TypeCast(make.Type(type), make.Identifier("obj")))); for (VariableElement ve : equalsFields) { 168 if (ve.asType().getKind().isPrimitive()) { 169 statements.add(make.If(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.MemberSelect(make.Identifier("other"), ve.getSimpleName())), make.Return(make.Identifier("false")), null)); } else { 172 ExpressionTree exp1 = make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.MemberSelect(make.Identifier("other"), ve.getSimpleName())); ExpressionTree exp2 = make.Binary(Tree.Kind.EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier("null")); ExpressionTree exp3 = make.Unary(Tree.Kind.LOGICAL_COMPLEMENT, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), "equals"), Collections.singletonList(make.MemberSelect(make.Identifier("other"), ve.getSimpleName())))); statements.add(make.If(make.Binary(Tree.Kind.CONDITIONAL_AND, exp1, make.Parenthesized(make.Binary(Tree.Kind.CONDITIONAL_OR, exp2, exp3))), make.Return(make.Identifier("false")), null)); } 178 } 179 statements.add(make.Return(make.Identifier("true"))); 180 BlockTree body = make.Block(statements, false); 181 182 return make.Method(make.Modifiers(mods), "equals", make.PrimitiveType(TypeKind.BOOLEAN), Collections.<TypeParameterTree> emptyList(), params, Collections.<ExpressionTree>emptyList(), body, null); } 184 185 private static MethodTree createHashCodeMethod(WorkingCopy wc, Iterable <? extends VariableElement> hashCodeFields, DeclaredType type) { 186 TreeMaker make = wc.getTreeMaker(); 187 Set <Modifier> mods = EnumSet.of(Modifier.PUBLIC); 188 189 int startNumber = generatePrimeNumber(2, 10); 190 int multiplyNumber = generatePrimeNumber(10, 100); 191 List <StatementTree> statements = new ArrayList <StatementTree>(); 192 statements.add(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "hash", make.PrimitiveType(TypeKind.INT), make.Literal(startNumber))); for (VariableElement ve : hashCodeFields) { 195 ExpressionTree variableRead; 196 switch (ve.asType().getKind()) { 197 case BYTE: 198 case CHAR: 199 case SHORT: 200 case INT: 201 variableRead = make.MemberSelect(make.Identifier("this"), ve.getSimpleName()); break; 203 case LONG: 204 variableRead = make.TypeCast(make.PrimitiveType(TypeKind.INT), make.Parenthesized(make.Binary(Tree.Kind.XOR, 205 make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Parenthesized(make.Binary(Tree.Kind.UNSIGNED_RIGHT_SHIFT, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Literal(32)))))); break; 208 case FLOAT: 209 variableRead = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("Float"), "floatToIntBits"), Collections.singletonList(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()))); break; 212 case DOUBLE: 213 ExpressionTree et = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("Double"), "doubleToLongBits"), Collections.singletonList(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()))); variableRead = make.TypeCast(make.PrimitiveType(TypeKind.INT), make.Parenthesized(make.Binary(Tree.Kind.XOR, 216 et, make.Parenthesized(make.Binary(Tree.Kind.UNSIGNED_RIGHT_SHIFT, et, make.Literal(32)))))); break; 218 case BOOLEAN: 219 variableRead = make.ConditionalExpression(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Literal(1), make.Literal(0)); break; 221 default: 222 variableRead = make.ConditionalExpression(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier("null")), make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), "hashCode"), Collections.<ExpressionTree>emptyList()), make.Literal(0)); 225 } 226 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)))); } 228 statements.add(make.Return(make.Identifier("hash"))); BlockTree body = make.Block(statements, false); 230 231 return make.Method(make.Modifiers(mods), "hashCode", make.PrimitiveType(TypeKind.INT), Collections.<TypeParameterTree> emptyList(), Collections.<VariableTree>emptyList(), Collections.<ExpressionTree>emptyList(), body, null); } 233 234 private static boolean isPrimeNumber(int n) { 235 int squareRoot = (int) Math.sqrt(n) + 1; 236 if (n % 2 == 0) 237 return false; 238 for (int cntr = 3; cntr < squareRoot; cntr++) { 239 if (n % cntr == 0) 240 return false; 241 } 242 return true; 243 } 244 245 private static int generatePrimeNumber(int lowerLimit, int higherLimit) { 246 Random r = new Random (System.currentTimeMillis()); 247 int proposed = r.nextInt(higherLimit - lowerLimit) + lowerLimit; 248 while (!isPrimeNumber(proposed)) 249 proposed++; 250 if (proposed > higherLimit) { 251 proposed--; 252 while (!isPrimeNumber(proposed)) 253 proposed--; 254 } 255 return proposed; 256 } 257 } 258 | Popular Tags |