001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * Portions Copyrighted 2007 Sun Microsystems, Inc.
027: */
028: package org.netbeans.modules.java.hints;
029:
030: import com.sun.source.tree.MethodTree;
031: import com.sun.source.tree.Tree;
032: import com.sun.source.tree.Tree.Kind;
033: import com.sun.source.util.TreePath;
034: import java.awt.EventQueue;
035: import java.io.IOException;
036: import java.util.Collections;
037: import java.util.EnumSet;
038: import java.util.List;
039: import java.util.Set;
040: import java.util.prefs.Preferences;
041: import javax.lang.model.element.Element;
042: import javax.lang.model.element.ElementKind;
043: import javax.lang.model.element.ExecutableElement;
044: import javax.swing.JComponent;
045: import javax.swing.JEditorPane;
046: import javax.swing.text.BadLocationException;
047: import javax.swing.text.Document;
048: import org.netbeans.api.java.source.CompilationInfo;
049: import org.netbeans.api.java.source.JavaSource;
050: import org.netbeans.api.java.source.ModificationResult;
051: import org.netbeans.api.java.source.Task;
052: import org.netbeans.api.java.source.TreePathHandle;
053: import org.netbeans.api.java.source.WorkingCopy;
054: import org.netbeans.modules.java.editor.codegen.EqualsHashCodeGenerator;
055: import org.netbeans.modules.java.editor.semantic.Utilities;
056: import org.netbeans.modules.java.hints.spi.AbstractHint;
057: import org.netbeans.spi.editor.hints.ChangeInfo;
058: import org.netbeans.spi.editor.hints.ErrorDescription;
059: import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
060: import org.netbeans.spi.editor.hints.Fix;
061: import org.openide.cookies.EditorCookie;
062: import org.openide.filesystems.FileObject;
063: import org.openide.loaders.DataObject;
064: import org.openide.loaders.DataObjectNotFoundException;
065: import org.openide.util.Exceptions;
066: import org.openide.util.NbBundle;
067:
068: /**
069: *
070: * @author Jaroslav tulach
071: */
072: public class MissingHashCode extends AbstractHint {
073: transient volatile boolean[] stop = { false };
074:
075: /** Creates a new instance of AddOverrideAnnotation */
076: public MissingHashCode() {
077: super (true, true, AbstractHint.HintSeverity.WARNING);
078: }
079:
080: public Set<Kind> getTreeKinds() {
081: return EnumSet.of(Kind.METHOD);
082: }
083:
084: public List<ErrorDescription> run(CompilationInfo compilationInfo,
085: TreePath treePath) {
086: stop = new boolean[1];
087: Element e = compilationInfo.getTrees().getElement(treePath);
088: if (e == null) {
089: return null;
090: }
091:
092: Element parent = e.getEnclosingElement();
093: ExecutableElement[] ret = EqualsHashCodeGenerator
094: .overridesHashCodeAndEquals(compilationInfo, parent,
095: stop);
096: if (e != ret[0] && e != ret[1]) {
097: return null;
098: }
099:
100: String addHint = null;
101: if (ret[0] == null && ret[1] != null) {
102: addHint = "MSG_GenEquals"; // NOI18N
103: }
104: if (ret[1] == null && ret[0] != null) {
105: addHint = "MSG_GenHashCode"; // NOI18N
106: }
107:
108: if (addHint != null) {
109:
110: List<Fix> fixes = Collections
111: .<Fix> singletonList(new FixImpl(addHint,
112: TreePathHandle.create(parent,
113: compilationInfo), compilationInfo
114: .getFileObject()));
115:
116: int[] span = compilationInfo.getTreeUtilities()
117: .findNameSpan((MethodTree) treePath.getLeaf());
118:
119: if (span != null) {
120: ErrorDescription ed = ErrorDescriptionFactory
121: .createErrorDescription(getSeverity()
122: .toEditorSeverity(), NbBundle
123: .getMessage(MissingHashCode.class,
124: addHint), fixes,
125: compilationInfo.getFileObject(),
126: span[0], span[1]);
127:
128: return Collections.singletonList(ed);
129: }
130: }
131:
132: return null;
133: }
134:
135: public String getId() {
136: return getClass().getName();
137: }
138:
139: public String getDisplayName() {
140: return NbBundle.getMessage(MissingHashCode.class,
141: "MSG_MissingHashCode");
142: }
143:
144: public String getDescription() {
145: return NbBundle.getMessage(MissingHashCode.class,
146: "HINT_MissingHashCode");
147: }
148:
149: public void cancel() {
150: stop[0] = true;
151: }
152:
153: public Preferences getPreferences() {
154: return null;
155: }
156:
157: @Override
158: public JComponent getCustomizer(Preferences node) {
159: return null;
160: }
161:
162: private static final class FixImpl implements Fix, Runnable,
163: Task<WorkingCopy> {
164: private TreePathHandle handle;
165: private FileObject file;
166: private String msg;
167: private boolean fieldFound;
168:
169: public FixImpl(String type, TreePathHandle handle,
170: FileObject file) {
171: this .handle = handle;
172: this .file = file;
173: this .msg = type;
174: }
175:
176: public String getText() {
177: return NbBundle.getMessage(MissingHashCode.class, msg);
178: }
179:
180: private static final Set<Kind> DECLARATION = EnumSet.of(
181: Kind.CLASS, Kind.METHOD, Kind.VARIABLE);
182:
183: public ChangeInfo implement() throws IOException {
184: ModificationResult result = JavaSource.forFileObject(file)
185: .runModificationTask(this );
186: if (fieldFound) {
187: EventQueue.invokeLater(this );
188: } else {
189: result.commit();
190: }
191:
192: return null;
193: }
194:
195: public void run() {
196: try {
197: EditorCookie cook = DataObject.find(file).getLookup()
198: .lookup(EditorCookie.class);
199: JEditorPane[] arr = cook.getOpenedPanes();
200: if (arr == null) {
201: return;
202: }
203: EqualsHashCodeGenerator.invokeEqualsHashCode(handle,
204: arr[0]);
205: } catch (DataObjectNotFoundException ex) {
206: Exceptions.printStackTrace(ex);
207: }
208: }
209:
210: public void run(WorkingCopy wc) throws Exception {
211: wc.toPhase(JavaSource.Phase.ELEMENTS_RESOLVED);
212: for (Element elem : handle.resolveElement(wc)
213: .getEnclosedElements()) {
214: if (elem.getKind() == ElementKind.FIELD) {
215: fieldFound = true;
216: return;
217: }
218: }
219: EqualsHashCodeGenerator.generateEqualsAndHashCode(wc,
220: handle.resolve(wc));
221: }
222:
223: @Override
224: public String toString() {
225: return "Fix";
226: }
227: }
228:
229: }
|