2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* Portions Copyrighted 2007 Sun Microsystems, Inc.
28
package org.netbeans.modules.java.hints;
30
import com.sun.source.tree.BinaryTree;
31
import com.sun.source.tree.BlockTree;
32
import com.sun.source.tree.ExpressionTree;
33
import com.sun.source.tree.IdentifierTree;
34
import com.sun.source.tree.IfTree;
35
import com.sun.source.tree.ParenthesizedTree;
36
import com.sun.source.tree.StatementTree;
37
import com.sun.source.tree.SynchronizedTree;
38
import com.sun.source.tree.Tree;
39
import com.sun.source.tree.Tree.Kind;
40
import com.sun.source.util.TreePath;
41
import java.io.IOException;
42
import java.util.Collections;
43
import java.util.EnumSet;
44
import java.util.List;
46
import java.util.prefs.Preferences;
47
import javax.swing.JComponent;
48
import javax.swing.text.BadLocationException;
49
import javax.swing.text.Document;
50
import org.netbeans.api.java.source.Task;
51
import org.netbeans.api.java.source.CompilationInfo;
52
import org.netbeans.api.java.source.JavaSource;
53
import org.netbeans.api.java.source.ModificationResult;
54
import org.netbeans.api.java.source.TreePathHandle;
55
import org.netbeans.api.java.source.WorkingCopy;
56
import org.netbeans.modules.java.hints.spi.AbstractHint;
57
import org.netbeans.spi.editor.hints.ChangeInfo;
58
import org.netbeans.spi.editor.hints.ErrorDescription;
59
import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
60
import org.netbeans.spi.editor.hints.Fix;
61
import org.openide.filesystems.FileObject;
62
import org.openide.util.Exceptions;
63
import org.openide.util.NbBundle;
67
* @author Jaroslav tulach
69
public class DoubleCheck extends AbstractHint {
70
private transient volatile boolean stop;
72
/** Creates a new instance of AddOverrideAnnotation */
73
public DoubleCheck() {
74
super( true, true, AbstractHint.HintSeverity.WARNING);
77
public Set<Kind> getTreeKinds() {
78
return EnumSet.of(Kind.SYNCHRONIZED);
81
public List<ErrorDescription> run(CompilationInfo compilationInfo,
85
Document doc = compilationInfo.getDocument();
91
Tree e = treePath.getLeaf();
92
if (e == null || e.getKind() != Kind.SYNCHRONIZED) {
96
SynchronizedTree synch = (SynchronizedTree)e;
97
IfTree outer = findOuterIf(compilationInfo, treePath);
103
for (StatementTree statement : synch.getBlock().getStatements()) {
104
if (sameIf(statement, outer)) {
105
same = (IfTree)statement;
116
TreePath outerPath = compilationInfo.getTrees().getPath(compilationInfo.getCompilationUnit(), outer);
118
List<Fix> fixes = Collections.<Fix>singletonList(new FixImpl(
119
TreePathHandle.create(treePath, compilationInfo),
120
TreePathHandle.create(outerPath, compilationInfo),
121
compilationInfo.getFileObject()
124
int span = (int)compilationInfo.getTrees().getSourcePositions().getStartPosition(
125
compilationInfo.getCompilationUnit(),
129
ErrorDescription ed = ErrorDescriptionFactory.createErrorDescription(
130
getSeverity().toEditorSeverity(),
131
NbBundle.getMessage(DoubleCheck.class, "MSG_FixDoubleCheck"), // NOI18N
134
doc.createPosition(span),
135
doc.createPosition(span + "synchronized".length()) // NOI18N
138
return Collections.singletonList(ed);
139
} catch (BadLocationException e) {
140
Exceptions.printStackTrace(e);
141
} catch (IOException e) {
142
Exceptions.printStackTrace(e);
148
public String getId() {
149
return getClass().getName();
152
public String getDisplayName() {
153
return NbBundle.getMessage(DoubleCheck.class, "MSG_DoubleCheck"); // NOI18N
156
public String getDescription() {
157
return NbBundle.getMessage(DoubleCheck.class, "HINT_DoubleCheck"); // NOI18N
160
public void cancel() {
164
public Preferences getPreferences() {
169
public JComponent getCustomizer(Preferences node) {
173
private IfTree findOuterIf(CompilationInfo compilationInfo, TreePath treePath) {
175
treePath = treePath.getParentPath();
176
if (treePath == null) {
179
Tree leaf = treePath.getLeaf();
181
if (leaf.getKind() == Kind.IF) {
185
if (leaf.getKind() == Kind.BLOCK) {
186
BlockTree b = (BlockTree)leaf;
187
if (b.getStatements().size() == 1) {
188
// ok, empty blocks can be around synchronized(this)
199
private boolean sameIf(StatementTree statement, IfTree second) {
200
if (statement.getKind() != Kind.IF) {
204
IfTree first = (IfTree)statement;
206
if (first.getElseStatement() != null) {
209
if (second.getElseStatement() != null) {
213
ExpressionTree varFirst = equalToNull(first.getCondition());
214
ExpressionTree varSecond = equalToNull(second.getCondition());
216
if (varFirst == null || varSecond == null) {
220
if (varFirst.getKind() == Kind.IDENTIFIER && varSecond.getKind() == Kind.IDENTIFIER) {
221
IdentifierTree idFirst = (IdentifierTree)varFirst;
222
IdentifierTree idSecond = (IdentifierTree)varSecond;
224
return idFirst.getName().equals(idSecond.getName());
230
private ExpressionTree equalToNull(ExpressionTree t) {
231
if (t.getKind() == Kind.PARENTHESIZED) {
232
ParenthesizedTree p = (ParenthesizedTree)t;
233
t = p.getExpression();
236
if (t.getKind() != Kind.EQUAL_TO) {
239
BinaryTree bt = (BinaryTree)t;
240
if (bt.getLeftOperand().getKind() == Kind.NULL_LITERAL && bt.getRightOperand().getKind() != Kind.NULL_LITERAL) {
241
return bt.getRightOperand();
243
if (bt.getLeftOperand().getKind() != Kind.NULL_LITERAL && bt.getRightOperand().getKind() == Kind.NULL_LITERAL) {
244
return bt.getLeftOperand();
249
private static final class FixImpl implements Fix, Task<WorkingCopy> {
250
private TreePathHandle synchHandle;
251
private TreePathHandle ifHandle;
252
private FileObject file;
254
public FixImpl(TreePathHandle synchHandle, TreePathHandle ifHandle, FileObject file) {
255
this.synchHandle = synchHandle;
256
this.ifHandle = ifHandle;
261
public String getText() {
262
return NbBundle.getMessage(DoubleCheck.class, "MSG_DoubleCheck"); // NOI18N
265
public ChangeInfo implement() throws IOException {
266
ModificationResult result = JavaSource.forFileObject(file).runModificationTask(this);
271
@Override public String toString() {
272
return "FixDoubleCheck"; // NOI18N
276
public void run(WorkingCopy wc) throws Exception {
277
wc.toPhase(JavaSource.Phase.RESOLVED);
278
Tree syncTree = synchHandle.resolve(wc).getLeaf();
279
Tree ifTree = ifHandle.resolve(wc).getLeaf();
280
wc.rewrite(ifTree, syncTree);