Lines 66-71
Link Here
|
66 |
public class WrongStringComparison extends AbstractHint { |
66 |
public class WrongStringComparison extends AbstractHint { |
67 |
|
67 |
|
68 |
private static final String TERNARY_NULL_CHECK = "ternary-null-check"; // NOI18N |
68 |
private static final String TERNARY_NULL_CHECK = "ternary-null-check"; // NOI18N |
|
|
69 |
private static final String STRING_LITERALS_FIRST = "string-literals-first"; //NOI18N |
69 |
private static final String STRING_TYPE = "java.lang.String"; // NOI18N |
70 |
private static final String STRING_TYPE = "java.lang.String"; // NOI18N |
70 |
private static final Set<Tree.Kind> TREE_KINDS = |
71 |
private static final Set<Tree.Kind> TREE_KINDS = |
71 |
EnumSet.<Tree.Kind>of( Tree.Kind.EQUAL_TO, Tree.Kind.NOT_EQUAL_TO ); |
72 |
EnumSet.<Tree.Kind>of( Tree.Kind.EQUAL_TO, Tree.Kind.NOT_EQUAL_TO ); |
Lines 109-116
Link Here
|
109 |
FileObject file = info.getFileObject(); |
110 |
FileObject file = info.getFileObject(); |
110 |
TreePathHandle tph = TreePathHandle.create(treePath, info); |
111 |
TreePathHandle tph = TreePathHandle.create(treePath, info); |
111 |
ArrayList<Fix> fixes = new ArrayList<Fix>(); |
112 |
ArrayList<Fix> fixes = new ArrayList<Fix>(); |
112 |
fixes.add(new WrongStringComparisonFix(file, tph, getTernaryNullCheck())); |
113 |
boolean reverseOperands = false; |
113 |
fixes.add(new WrongStringComparisonFix(file, tph, null)); //no null check |
114 |
if (bt.getLeftOperand().getKind() != Tree.Kind.STRING_LITERAL) { |
|
|
115 |
if (bt.getRightOperand().getKind() == Tree.Kind.STRING_LITERAL) { |
116 |
if (getStringLiteralsFirst()) { |
117 |
reverseOperands = true; |
118 |
} else { |
119 |
fixes.add(new WrongStringComparisonFix(file, tph, WrongStringComparisonFix.Kind.NULL_CHECK)); |
120 |
} |
121 |
} else { |
122 |
fixes.add(new WrongStringComparisonFix(file, tph, WrongStringComparisonFix.Kind.ternaryNullCheck(getTernaryNullCheck()))); |
123 |
} |
124 |
} |
125 |
fixes.add(new WrongStringComparisonFix(file, tph, WrongStringComparisonFix.Kind.reverseOperands(reverseOperands))); |
114 |
return Collections.<ErrorDescription>singletonList( |
126 |
return Collections.<ErrorDescription>singletonList( |
115 |
ErrorDescriptionFactory.createErrorDescription( |
127 |
ErrorDescriptionFactory.createErrorDescription( |
116 |
getSeverity().toEditorSeverity(), |
128 |
getSeverity().toEditorSeverity(), |
Lines 141-150
Link Here
|
141 |
return NbBundle.getMessage(WrongStringComparison.class, "DSC_WrongStringComparison"); // NOI18N |
153 |
return NbBundle.getMessage(WrongStringComparison.class, "DSC_WrongStringComparison"); // NOI18N |
142 |
} |
154 |
} |
143 |
|
155 |
|
144 |
public boolean getTernaryNullCheck() { |
|
|
145 |
return getTernaryNullCheck(getPreferences(null)); |
146 |
} |
147 |
|
148 |
@Override |
156 |
@Override |
149 |
public JComponent getCustomizer(Preferences node) { |
157 |
public JComponent getCustomizer(Preferences node) { |
150 |
return new WrongStringComparisonCustomizer(node); |
158 |
return new WrongStringComparisonCustomizer(node); |
Lines 179-213
Link Here
|
179 |
return CopyFinder.isDuplicate(info, originalPath, correctPath, cancel); |
187 |
return CopyFinder.isDuplicate(info, originalPath, correctPath, cancel); |
180 |
} |
188 |
} |
181 |
|
189 |
|
|
|
190 |
boolean getTernaryNullCheck() { |
191 |
return getTernaryNullCheck(getPreferences(null)); |
192 |
} |
193 |
|
194 |
boolean getStringLiteralsFirst() { |
195 |
return getStringLiteralsFirst(getPreferences(null)); |
196 |
} |
197 |
|
182 |
static boolean getTernaryNullCheck(Preferences p) { |
198 |
static boolean getTernaryNullCheck(Preferences p) { |
183 |
return p.getBoolean(TERNARY_NULL_CHECK, true); |
199 |
return p.getBoolean(TERNARY_NULL_CHECK, true); |
184 |
} |
200 |
} |
185 |
|
201 |
|
|
|
202 |
static boolean getStringLiteralsFirst(Preferences p) { |
203 |
return p.getBoolean(STRING_LITERALS_FIRST, true); |
204 |
} |
205 |
|
186 |
static void setTernaryNullCheck(Preferences p, boolean selected) { |
206 |
static void setTernaryNullCheck(Preferences p, boolean selected) { |
187 |
p.putBoolean(TERNARY_NULL_CHECK, selected); |
207 |
p.putBoolean(TERNARY_NULL_CHECK, selected); |
188 |
} |
208 |
} |
189 |
|
209 |
|
|
|
210 |
static void setStringLiteralsFirst(Preferences p, boolean selected) { |
211 |
p.putBoolean(STRING_LITERALS_FIRST, selected); |
212 |
} |
213 |
|
190 |
static class WrongStringComparisonFix implements Fix, Task<WorkingCopy> { |
214 |
static class WrongStringComparisonFix implements Fix, Task<WorkingCopy> { |
191 |
|
215 |
|
192 |
protected FileObject file; |
216 |
protected FileObject file; |
193 |
protected TreePathHandle tph; |
217 |
protected TreePathHandle tph; |
194 |
protected Boolean nullCheck; |
218 |
protected Kind kind; |
195 |
|
219 |
|
196 |
public WrongStringComparisonFix(FileObject file, TreePathHandle tph, Boolean nullCheck) { |
220 |
public WrongStringComparisonFix(FileObject file, TreePathHandle tph, Kind kind) { |
197 |
this.file = file; |
221 |
this.file = file; |
198 |
this.tph = tph; |
222 |
this.tph = tph; |
199 |
this.nullCheck = nullCheck; |
223 |
this.kind = kind; |
200 |
} |
224 |
} |
201 |
|
225 |
|
202 |
public String getText() { |
226 |
public String getText() { |
203 |
if (nullCheck == null) { |
227 |
switch (kind) { |
204 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NoNullCheck"); // NOI18N |
228 |
case REVERSE_OPERANDS: |
205 |
} else { |
229 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_ReverseOperands"); // NOI18N |
206 |
if (nullCheck) { |
230 |
case NO_NULL_CHECK: |
|
|
231 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NoNullCheck"); // NOI18N |
232 |
case NULL_CHECK_TERNARY: |
207 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_TernaryNullCheck"); // NOI18N |
233 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_TernaryNullCheck"); // NOI18N |
208 |
} else { |
234 |
default: |
209 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NullCheck"); // NOI18N |
235 |
return NbBundle.getMessage(WrongStringComparison.class, "FIX_WrongStringComparison_NullCheck"); // NOI18N |
210 |
} |
|
|
211 |
} |
236 |
} |
212 |
} |
237 |
} |
213 |
|
238 |
|
Lines 222-229
Link Here
|
222 |
return "[WrongStringComparisonFix:" + getText() + "]"; |
247 |
return "[WrongStringComparisonFix:" + getText() + "]"; |
223 |
} |
248 |
} |
224 |
|
249 |
|
225 |
|
|
|
226 |
|
227 |
public void run(WorkingCopy copy) throws Exception { |
250 |
public void run(WorkingCopy copy) throws Exception { |
228 |
copy.toPhase(JavaSource.Phase.PARSED); |
251 |
copy.toPhase(JavaSource.Phase.PARSED); |
229 |
TreePath path = tph.resolve(copy); |
252 |
TreePath path = tph.resolve(copy); |
Lines 232-267
Link Here
|
232 |
BinaryTree oldTree = (BinaryTree) path.getLeaf(); |
255 |
BinaryTree oldTree = (BinaryTree) path.getLeaf(); |
233 |
ExpressionTree left = oldTree.getLeftOperand(); |
256 |
ExpressionTree left = oldTree.getLeftOperand(); |
234 |
ExpressionTree right = oldTree.getRightOperand(); |
257 |
ExpressionTree right = oldTree.getRightOperand(); |
235 |
ExpressionTree leftEquals = make.MemberSelect(left, "equals"); // NOI18N |
|
|
236 |
ExpressionTree leftEqualsRight = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), leftEquals, Collections.singletonList(right)); |
237 |
if (oldTree.getKind() == Tree.Kind.NOT_EQUAL_TO) { |
238 |
leftEqualsRight = make.Unary(Tree.Kind.LOGICAL_COMPLEMENT, leftEqualsRight); |
239 |
} |
240 |
ExpressionTree newTree; |
258 |
ExpressionTree newTree; |
241 |
if (nullCheck == null) { |
259 |
if (kind == Kind.REVERSE_OPERANDS) { |
242 |
// str1.equals(str2) |
260 |
// "str2".equals(str1) |
243 |
newTree = leftEqualsRight; |
261 |
ExpressionTree rightEquals = make.MemberSelect(right, "equals"); // NOI18N |
|
|
262 |
ExpressionTree rightEqualsLeft = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), rightEquals, Collections.singletonList(left)); |
263 |
rightEqualsLeft = matchSign(make, oldTree, rightEqualsLeft); |
264 |
newTree = rightEqualsLeft; |
244 |
} else { |
265 |
} else { |
245 |
ExpressionTree leftEqNull = make.Binary(Tree.Kind.EQUAL_TO, left, make.Identifier("null")); // NOI18N |
266 |
ExpressionTree leftEquals = make.MemberSelect(left, "equals"); // NOI18N |
246 |
ExpressionTree rightEqNull = make.Binary(oldTree.getKind(), right, make.Identifier("null")); // NOI18N |
267 |
ExpressionTree leftEqualsRight = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), leftEquals, Collections.singletonList(right)); |
247 |
if (nullCheck) { |
268 |
leftEqualsRight = matchSign(make, oldTree, leftEqualsRight); |
248 |
// str1 == null ? str2 == null : str1.equals(str2) |
269 |
if (kind == Kind.NO_NULL_CHECK) { |
249 |
newTree = make.ConditionalExpression(leftEqNull, rightEqNull, leftEqualsRight); |
270 |
// str1.equals(str2) |
|
|
271 |
newTree = leftEqualsRight; |
250 |
} else { |
272 |
} else { |
251 |
// (str1 == null && str2 == null) || (str1 != null && str1.equals(str2)) |
273 |
ExpressionTree leftEqNull = make.Binary(Tree.Kind.EQUAL_TO, left, make.Identifier("null")); // NOI18N |
252 |
ExpressionTree leftNeNull = make.Binary(Tree.Kind.NOT_EQUAL_TO, left, make.Identifier("null")); // NOI18N |
274 |
ExpressionTree rightEqNull = make.Binary(oldTree.getKind(), right, make.Identifier("null")); // NOI18N |
253 |
ExpressionTree orLeft = make.Parenthesized(make.Binary(Tree.Kind.CONDITIONAL_AND, leftEqNull, rightEqNull)); |
275 |
if (kind == Kind.NULL_CHECK_TERNARY) { |
254 |
ExpressionTree orRight = make.Parenthesized(make.Binary(Tree.Kind.CONDITIONAL_AND, leftNeNull, leftEqualsRight)); |
276 |
// str1 == null ? str2 == null : str1.equals(str2) |
255 |
newTree = make.Binary(Tree.Kind.CONDITIONAL_OR, orLeft, orRight); |
277 |
newTree = make.ConditionalExpression(leftEqNull, rightEqNull, leftEqualsRight); |
256 |
} |
278 |
} else { |
257 |
if (path.getParentPath().getLeaf().getKind() != Tree.Kind.PARENTHESIZED) { |
279 |
ExpressionTree leftNeNull = make.Binary(Tree.Kind.NOT_EQUAL_TO, left, make.Identifier("null")); // NOI18N |
258 |
newTree = make.Parenthesized(newTree); |
280 |
ExpressionTree leftNeNullAndLeftEqualsRight = make.Binary(Tree.Kind.CONDITIONAL_AND, leftNeNull, leftEqualsRight); |
|
|
281 |
if (right.getKind() == Tree.Kind.STRING_LITERAL) { |
282 |
// str1 != null && str1.equals("str2") |
283 |
newTree = leftNeNullAndLeftEqualsRight; |
284 |
} else { |
285 |
// (str1 == null && str2 == null) || (str1 != null && str1.equals(str2)) |
286 |
ExpressionTree leftEqNullAndRightEqNull = make.Binary(Tree.Kind.CONDITIONAL_AND, leftEqNull, rightEqNull); |
287 |
newTree = make.Binary(Tree.Kind.CONDITIONAL_OR, make.Parenthesized(leftEqNullAndRightEqNull), make.Parenthesized(leftNeNullAndLeftEqualsRight)); |
288 |
} |
289 |
} |
290 |
if (path.getParentPath().getLeaf().getKind() != Tree.Kind.PARENTHESIZED) { |
291 |
newTree = make.Parenthesized(newTree); |
292 |
} |
259 |
} |
293 |
} |
260 |
} |
294 |
} |
261 |
copy.rewrite(oldTree, newTree); |
295 |
copy.rewrite(oldTree, newTree); |
262 |
} |
296 |
} |
263 |
} |
297 |
} |
264 |
|
298 |
|
|
|
299 |
ExpressionTree matchSign(TreeMaker make, BinaryTree oldTree, ExpressionTree et) { |
300 |
if (oldTree.getKind() == Tree.Kind.NOT_EQUAL_TO) { |
301 |
return make.Unary(Tree.Kind.LOGICAL_COMPLEMENT, et); |
302 |
} else { |
303 |
return et; |
304 |
} |
305 |
} |
306 |
|
307 |
enum Kind { |
308 |
REVERSE_OPERANDS, |
309 |
NO_NULL_CHECK, |
310 |
NULL_CHECK, |
311 |
NULL_CHECK_TERNARY; |
312 |
|
313 |
public static Kind ternaryNullCheck(boolean ternary) { |
314 |
return ternary ? NULL_CHECK_TERNARY : NULL_CHECK; |
315 |
} |
316 |
|
317 |
public static Kind reverseOperands(boolean reverseOperands) { |
318 |
return reverseOperands ? REVERSE_OPERANDS : NO_NULL_CHECK; |
319 |
} |
320 |
|
321 |
} |
322 |
|
265 |
} |
323 |
} |
266 |
|
324 |
|
267 |
} |
325 |
} |