1 11 package org.eclipse.jdt.internal.corext.dom.fragments; 12 13 import java.util.ArrayList ; 14 import java.util.Collections ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 18 import org.eclipse.text.edits.TextEditGroup; 19 20 import org.eclipse.core.runtime.Assert; 21 22 import org.eclipse.jdt.core.ICompilationUnit; 23 import org.eclipse.jdt.core.JavaModelException; 24 import org.eclipse.jdt.core.dom.ASTNode; 25 import org.eclipse.jdt.core.dom.CompilationUnit; 26 import org.eclipse.jdt.core.dom.Expression; 27 import org.eclipse.jdt.core.dom.InfixExpression; 28 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 29 30 import org.eclipse.jdt.internal.corext.SourceRange; 31 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; 32 import org.eclipse.jdt.internal.corext.dom.GenericVisitor; 33 import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher; 34 35 class AssociativeInfixExpressionFragment extends ASTFragment implements IExpressionFragment { 36 37 private final List fOperands; 38 private final InfixExpression fGroupRoot; 39 40 public static IExpressionFragment createSubPartFragmentBySourceRange(InfixExpression node, SourceRange range, ICompilationUnit cu) throws JavaModelException { 41 Assert.isNotNull(node); 42 Assert.isNotNull(range); 43 Assert.isTrue(!range.covers(node)); 44 Assert.isTrue(new SourceRange(node).covers(range)); 45 46 if(!isAssociativeInfix(node)) 47 return null; 48 49 InfixExpression groupRoot= findGroupRoot(node); 50 Assert.isTrue(isAGroupRoot(groupRoot)); 51 52 List groupMembers= AssociativeInfixExpressionFragment.findGroupMembersInOrderFor(groupRoot); 53 List subGroup= findSubGroupForSourceRange(groupMembers, range); 54 if(subGroup.isEmpty() || rangeIncludesExtraNonWhitespace(range, subGroup, cu)) 55 return null; 56 57 return new AssociativeInfixExpressionFragment(groupRoot, subGroup); 58 } 59 60 public static IExpressionFragment createFragmentForFullSubtree(InfixExpression node) { 61 Assert.isNotNull(node); 62 63 if(!isAssociativeInfix(node)) 64 return null; 65 66 InfixExpression groupRoot= findGroupRoot(node); 67 Assert.isTrue(isAGroupRoot(groupRoot)); 68 69 List groupMembers= AssociativeInfixExpressionFragment.findGroupMembersInOrderFor(node); 70 71 return new AssociativeInfixExpressionFragment(groupRoot, groupMembers); 72 } 73 74 private static InfixExpression findGroupRoot(InfixExpression node) { 75 Assert.isTrue(isAssociativeInfix(node)); 76 77 while(!isAGroupRoot(node)) { 78 ASTNode parent= node.getParent(); 79 80 Assert.isNotNull(parent); 81 Assert.isTrue(isAssociativeInfix(parent)); 82 Assert.isTrue(((InfixExpression) parent).getOperator() == node.getOperator()); 83 84 node= (InfixExpression) parent; 85 } 86 87 return node; 88 } 89 90 private static List findSubGroupForSourceRange(List group, SourceRange range) { 91 Assert.isTrue(!group.isEmpty()); 92 93 List subGroup= new ArrayList (); 94 95 boolean entered= false, exited= false; 96 if(range.getOffset() == ((ASTNode)group.get(0)).getStartPosition()) 97 entered= true; 98 for(int i= 0; i < group.size() - 1; i++) { 99 ASTNode member= (ASTNode) group.get(i); 100 ASTNode nextMember= (ASTNode) group.get(i + 1); 101 102 if(entered) { 103 subGroup.add(member); 104 if(rangeEndsBetween(range, member, nextMember)) { 105 exited= true; 106 break; 107 } 108 109 } else { 110 if(rangeStartsBetween(range, member, nextMember)) 111 entered= true; 112 } 113 } 114 ASTNode lastGroupMember= (ASTNode)group.get(group.size() - 1); 115 if(range.getEndExclusive() == new SourceRange(lastGroupMember).getEndExclusive()) { 116 subGroup.add(lastGroupMember); 117 exited= true; 118 } 119 120 if(!exited) 121 return new ArrayList (0); 122 return subGroup; 123 } 124 125 private static boolean rangeStartsBetween(SourceRange range, ASTNode first, ASTNode next) { 126 int pos= range.getOffset(); 127 return first.getStartPosition() + first.getLength() <= pos 128 && pos <= next.getStartPosition(); 129 } 130 131 private static boolean rangeEndsBetween(SourceRange range, ASTNode first, ASTNode next) { 132 int pos= range.getEndExclusive(); 133 return first.getStartPosition() + first.getLength() <= pos 134 && pos <= next.getStartPosition(); 135 } 136 137 private static boolean rangeIncludesExtraNonWhitespace(SourceRange range, List operands, ICompilationUnit cu) throws JavaModelException { 138 return Util.rangeIncludesNonWhitespaceOutsideRange(range, getRangeOfOperands(operands), cu.getBuffer()); 139 } 140 141 private static SourceRange getRangeOfOperands(List operands) { 142 Expression first= (Expression) operands.get(0); 143 Expression last= (Expression) operands.get(operands.size() - 1); 144 return new SourceRange(first.getStartPosition(), last.getStartPosition() + last.getLength() - first.getStartPosition()); 145 } 146 147 public IASTFragment[] getMatchingFragmentsWithNode(ASTNode node) { 148 IASTFragment fragmentForNode= ASTFragmentFactory.createFragmentForFullSubtree(node); 149 if (fragmentForNode instanceof AssociativeInfixExpressionFragment) { 150 AssociativeInfixExpressionFragment kin= (AssociativeInfixExpressionFragment)fragmentForNode; 151 return kin.getSubFragmentsWithMyNodeMatching(this); 152 } else { 153 return new IASTFragment[0]; 154 } 155 } 156 157 160 private static List getMatchingContiguousNodeSubsequences(List source, List toMatch) { 161 163 List subsequences= new ArrayList (); 164 165 for(int i= 0; i < source.size();) { 166 if(matchesAt(i, source, toMatch)) { 167 subsequences.add(source.subList(i, i + toMatch.size())); 168 i += toMatch.size(); 169 } else 170 i++; 171 } 172 173 return subsequences; 174 } 175 176 private static boolean matchesAt(int index, List subject, List toMatch) { 177 if(index + toMatch.size() > subject.size()) 178 return false; 179 for(int i= 0; i < toMatch.size(); i++, index++) { 180 if(!JdtASTMatcher.doNodesMatch( 181 (ASTNode) subject.get(index), (ASTNode) toMatch.get(i) 182 ) 183 ) 184 return false; 185 } 186 return true; 187 } 188 189 private static boolean isAGroupRoot(ASTNode node) { 190 Assert.isNotNull(node); 191 192 return isAssociativeInfix(node) 193 && !isParentInfixWithSameOperator((InfixExpression) node); 194 } 195 196 private static boolean isAssociativeInfix(ASTNode node) { 197 return node instanceof InfixExpression && isOperatorAssociative(((InfixExpression)node).getOperator()); 198 } 199 200 private static boolean isParentInfixWithSameOperator(InfixExpression node) { 201 return node.getParent() instanceof InfixExpression 202 && ((InfixExpression) node.getParent()).getOperator() == node.getOperator(); 203 } 204 205 private static boolean isOperatorAssociative(InfixExpression.Operator operator) { 206 return operator == InfixExpression.Operator.PLUS 207 || operator == InfixExpression.Operator.TIMES 208 || operator == InfixExpression.Operator.XOR 209 || operator == InfixExpression.Operator.OR 210 || operator == InfixExpression.Operator.AND 211 || operator == InfixExpression.Operator.CONDITIONAL_OR 212 || operator == InfixExpression.Operator.CONDITIONAL_AND; 213 } 214 215 private AssociativeInfixExpressionFragment(InfixExpression groupRoot, List operands) { 216 Assert.isTrue(isAGroupRoot(groupRoot)); 217 Assert.isTrue(operands.size() >= 2); 218 fGroupRoot= groupRoot; 219 fOperands= Collections.unmodifiableList(operands); 220 } 221 222 public boolean matches(IASTFragment other) { 223 if (! other.getClass().equals(getClass())) 224 return false; 225 226 AssociativeInfixExpressionFragment otherOfKind= (AssociativeInfixExpressionFragment) other; 227 return getOperator() == otherOfKind.getOperator() 228 && doOperandsMatch(otherOfKind); 229 } 230 231 private boolean doOperandsMatch(AssociativeInfixExpressionFragment other) { 232 if (getOperands().size() != other.getOperands().size()) 233 return false; 234 235 Iterator myOperands= getOperands().iterator(); 236 Iterator othersOperands= other.getOperands().iterator(); 237 238 while (myOperands.hasNext() && othersOperands.hasNext()) { 239 ASTNode myOperand= (ASTNode) myOperands.next(); 240 ASTNode othersOperand= (ASTNode) othersOperands.next(); 241 242 if (! JdtASTMatcher.doNodesMatch(myOperand, othersOperand)) 243 return false; 244 } 245 246 return true; 247 } 248 249 public IASTFragment[] getSubFragmentsMatching(IASTFragment toMatch) { 250 return union( 251 getSubFragmentsWithMyNodeMatching(toMatch), 252 getSubFragmentsWithAnotherNodeMatching(toMatch) 253 ); 254 } 255 256 private IASTFragment[] getSubFragmentsWithMyNodeMatching(IASTFragment toMatch) { 257 if(toMatch.getClass() != getClass()) 258 return new IASTFragment[0]; 259 260 AssociativeInfixExpressionFragment kinToMatch= (AssociativeInfixExpressionFragment) toMatch; 261 if(kinToMatch.getOperator() != getOperator()) 262 return new IASTFragment[0]; 263 264 List matchingSubsequences= 265 getMatchingContiguousNodeSubsequences( 266 getOperands(), 267 kinToMatch.getOperands() 268 ); 269 270 IASTFragment[] matches= new IASTFragment[matchingSubsequences.size()]; 271 for(int i= 0; i < matchingSubsequences.size(); i++) { 272 IASTFragment match= new AssociativeInfixExpressionFragment( 273 fGroupRoot, 274 (List ) matchingSubsequences.get(i) 275 ); 276 Assert.isTrue(match.matches(toMatch) || toMatch.matches(match)); 277 matches[i]= match; 278 } 279 return matches; 280 } 281 282 private IASTFragment[] getSubFragmentsWithAnotherNodeMatching(IASTFragment toMatch) { 283 IASTFragment[] result= new IASTFragment[0]; 284 for (Iterator iter= getOperands().iterator(); iter.hasNext();) { 285 ASTNode operand= (ASTNode) iter.next(); 286 result= union(result, ASTMatchingFragmentFinder.findMatchingFragments(operand, (ASTFragment)toMatch)); 287 } 288 return result; 289 } 290 private static IASTFragment[] union(IASTFragment[] a1, IASTFragment[] a2) { 291 IASTFragment[] union= new IASTFragment[a1.length + a2.length]; 292 System.arraycopy(a1, 0, union, 0, a1.length); 293 System.arraycopy(a2, 0, union, a1.length, a2.length); 294 return union; 295 296 } 307 308 309 314 public Expression getAssociatedExpression() { 315 return fGroupRoot; 316 } 317 318 323 public ASTNode getAssociatedNode() { 324 return fGroupRoot; 325 } 326 327 public InfixExpression getGroupRoot() { 328 return fGroupRoot; 329 } 330 331 public int getLength() { 332 return getEndPositionExclusive() - getStartPosition(); 333 } 334 335 private int getEndPositionExclusive() { 336 List operands= getOperands(); 337 ASTNode lastNode= (ASTNode) operands.get(operands.size() - 1); 338 return lastNode.getStartPosition() + lastNode.getLength(); 339 } 340 341 public int getStartPosition() { 342 return ((ASTNode) getOperands().get(0)).getStartPosition(); 343 } 344 345 public List getOperands() { 346 return fOperands; 347 } 348 349 public InfixExpression.Operator getOperator() { 350 return fGroupRoot.getOperator(); 351 } 352 353 public Expression createCopyTarget(ASTRewrite rewrite) throws JavaModelException { 354 List allOperands= findGroupMembersInOrderFor(fGroupRoot); 355 if (allOperands.size() == fOperands.size()) { 356 return (Expression) rewrite.createCopyTarget(fGroupRoot); 357 } 358 359 CompilationUnit root= (CompilationUnit) fGroupRoot.getRoot(); 360 ICompilationUnit cu= (ICompilationUnit) root.getJavaElement(); 361 String source= cu.getBuffer().getText(getStartPosition(), getLength()); 362 return (Expression) rewrite.createStringPlaceholder(source, ASTNode.INFIX_EXPRESSION); 363 364 } 378 379 public void replace(ASTRewrite rewrite, ASTNode replacement, TextEditGroup textEditGroup) { 380 List allOperands= findGroupMembersInOrderFor(fGroupRoot); 381 if (allOperands.size() == fOperands.size()) { 382 rewrite.replace(fGroupRoot, replacement, textEditGroup); 383 return; 384 } 385 386 int first= allOperands.indexOf(fOperands.get(0)); 389 int after= first + fOperands.size(); 390 ArrayList newOperands= new ArrayList (); 391 for (int i= 0; i < allOperands.size(); i++) { 392 if (i < first || after <= i) { 393 newOperands.add(rewrite.createCopyTarget((Expression) allOperands.get(i))); 394 } else { 395 newOperands.add(replacement); 396 i= after - 1; 397 } 398 } 399 Expression newExpression= ASTNodeFactory.newInfixExpression(rewrite.getAST(), getOperator(), newOperands); 400 rewrite.replace(getGroupRoot(), newExpression, textEditGroup); 401 } 402 403 private static ArrayList findGroupMembersInOrderFor(InfixExpression groupRoot) { 404 return new GroupMemberFinder(groupRoot).fMembersInOrder; 405 } 406 407 private static class GroupMemberFinder extends GenericVisitor { 408 private ArrayList fMembersInOrder= new ArrayList (); 409 private InfixExpression fGroupRoot; 410 411 public GroupMemberFinder(InfixExpression groupRoot) { 412 super(true); 413 Assert.isTrue(isAssociativeInfix(groupRoot)); 414 fGroupRoot= groupRoot; 415 fGroupRoot.accept(this); 416 } 417 protected boolean visitNode(ASTNode node) { 418 if (node instanceof InfixExpression && ((InfixExpression) node).getOperator() == fGroupRoot.getOperator()) 419 return true; 420 421 fMembersInOrder.add(node); 422 return false; 423 } 424 } 425 426 public int hashCode() { 427 return fGroupRoot.hashCode(); 428 } 429 430 public boolean equals(Object obj) { 431 if (this == obj) 432 return true; 433 if (obj == null) 434 return false; 435 if (getClass() != obj.getClass()) 436 return false; 437 AssociativeInfixExpressionFragment other= (AssociativeInfixExpressionFragment) obj; 438 return fGroupRoot.equals(other.fGroupRoot) && fOperands.equals(other.fOperands); 439 } 440 } 441 | Popular Tags |