KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > java > editor > codegen > EqualsHashCodeGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

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 JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.util.ArrayList JavaDoc;
34 import java.util.Collections JavaDoc;
35 import java.util.EnumSet JavaDoc;
36 import java.util.List JavaDoc;
37 import java.util.Random JavaDoc;
38 import java.util.Set JavaDoc;
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 JavaDoc;
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 /**
63  *
64  * @author Dusan Balek
65  */

66 public class EqualsHashCodeGenerator implements CodeGenerator {
67
68     public static class Factory implements CodeGenerator.Factory {
69         
70         Factory() {
71         }
72         
73         public Iterable JavaDoc<? extends CodeGenerator> create(CompilationController controller, TreePath path) throws IOException JavaDoc {
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 JavaDoc<ElementNode.Description> descriptions = new ArrayList JavaDoc<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     /** Creates a new instance of EqualsHashCodeGenerator */
93     private EqualsHashCodeGenerator(ElementNode.Description description) {
94         this.description = description;
95     }
96
97     public String JavaDoc getDisplayName() {
98         return org.openide.util.NbBundle.getMessage(EqualsHashCodeGenerator.class, "LBL_equals_and_hashcode"); //NOI18N
99
}
100
101     public void invoke(JTextComponent JavaDoc component) {
102         final EqualsHashCodePanel panel = new EqualsHashCodePanel(description);
103         DialogDescriptor dialogDescriptor = new DialogDescriptor(panel, NbBundle.getMessage(ConstructorGenerator.class, "LBL_generate_equals_and_hashcode")); //NOI18N
104
Dialog JavaDoc 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 JavaDoc {
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 JavaDoc<VariableElement> equalsElements = new ArrayList JavaDoc<VariableElement>();
127                             for (ElementHandle<? extends Element> elementHandle : panel.getEqualsVariables())
128                                 equalsElements.add((VariableElement)elementHandle.resolve(copy));
129                             ArrayList JavaDoc<VariableElement> hashCodeElements = new ArrayList JavaDoc<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 JavaDoc ex) {
136                     Exceptions.printStackTrace(ex);
137                 }
138             }
139         }
140     }
141     
142     private static void generateEqualsAndHashCode(WorkingCopy wc, TreePath path, Iterable JavaDoc<? extends VariableElement> equalsFields, Iterable JavaDoc<? 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 JavaDoc<? extends VariableElement> equalsFields, DeclaredType type) {
155         TreeMaker make = wc.getTreeMaker();
156         Set JavaDoc<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
157         List JavaDoc<VariableTree> params = Collections.singletonList(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "obj", make.Type(wc.getElements().getTypeElement("java.lang.Object").asType()), null)); //NOI18N
158

159         List JavaDoc<StatementTree> statements = new ArrayList JavaDoc<StatementTree>();
160         //if (obj == null) return false;
161
statements.add(make.If(make.Binary(Tree.Kind.EQUAL_TO, make.Identifier("obj"), make.Identifier("null")), make.Return(make.Identifier("false")), null)); //NOI18N
162
//if (getClass() != obj.getClass()) return false;
163
statements.add(make.If(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.Identifier("getClass"), Collections.<ExpressionTree>emptyList()), //NOI18N
164
make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("obj"), "getClass"), Collections.<ExpressionTree>emptyList())), make.Return(make.Identifier("false")), null)); //NOI18N
165
//<this type> other = (<this type>) o;
166
statements.add(make.Variable(make.Modifiers(EnumSet.of(Modifier.FINAL)), "other", make.Type(type), make.TypeCast(make.Type(type), make.Identifier("obj")))); //NOI18N
167
for (VariableElement ve : equalsFields) {
168             if (ve.asType().getKind().isPrimitive()) {
169                 //if (this.<var> != other.<var>) return false;
170
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)); //NOI18N
171
} else {
172                 //if (this.<var> != other.<var> && (this.<var> == null || !this.<var>.equals(other.<var>))) return false;
173
ExpressionTree exp1 = make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.MemberSelect(make.Identifier("other"), ve.getSimpleName())); //NOI18N
174
ExpressionTree exp2 = make.Binary(Tree.Kind.EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier("null")); //NOI18N
175
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())))); //NOI18N
176
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)); //NOI18N
177
}
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); //NOI18N
183
}
184     
185     private static MethodTree createHashCodeMethod(WorkingCopy wc, Iterable JavaDoc<? extends VariableElement> hashCodeFields, DeclaredType type) {
186         TreeMaker make = wc.getTreeMaker();
187         Set JavaDoc<Modifier> mods = EnumSet.of(Modifier.PUBLIC);
188
189         int startNumber = generatePrimeNumber(2, 10);
190         int multiplyNumber = generatePrimeNumber(10, 100);
191         List JavaDoc<StatementTree> statements = new ArrayList JavaDoc<StatementTree>();
192         //int hash = <startNumber>;
193
statements.add(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "hash", make.PrimitiveType(TypeKind.INT), make.Literal(startNumber))); //NOI18N
194
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()); //NOI18N
202
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, //NOI18N
206
make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Literal(32)))))); //NOI18N
207
break;
208                 case FLOAT:
209                     variableRead = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("Float"), "floatToIntBits"), //NOI18N
210
Collections.singletonList(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()))); //NOI18N
211
break;
212                 case DOUBLE:
213                     ExpressionTree et = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("Double"), "doubleToLongBits"), //NOI18N
214
Collections.singletonList(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()))); //NOI18N
215
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)))))); //NOI18N
217
break;
218                 case BOOLEAN:
219                     variableRead = make.ConditionalExpression(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Literal(1), make.Literal(0)); //NOI18N
220
break;
221                 default:
222                     variableRead = make.ConditionalExpression(make.Binary(Tree.Kind.NOT_EQUAL_TO, make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), make.Identifier("null")), //NOI18N
223
make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.MemberSelect(make.Identifier("this"), ve.getSimpleName()), "hashCode"), Collections.<ExpressionTree>emptyList()), //NOI18N
224
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)))); //NOI18N
227
}
228         statements.add(make.Return(make.Identifier("hash"))); //NOI18N
229
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); //NOI18N
232
}
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 JavaDoc r = new Random JavaDoc(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