KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > dom > fragments > AssociativeInfixExpressionFragment


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.corext.dom.fragments;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Collections JavaDoc;
15 import java.util.Iterator JavaDoc;
16 import java.util.List JavaDoc;
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 JavaDoc/*<Expression>*/ 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 JavaDoc/*<Expression>*/ groupMembers= AssociativeInfixExpressionFragment.findGroupMembersInOrderFor(groupRoot);
53         List JavaDoc/*<Expression>*/ 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 JavaDoc/*<Expression>*/ 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 JavaDoc findSubGroupForSourceRange(List JavaDoc/*<Expression>*/ group, SourceRange range) {
91         Assert.isTrue(!group.isEmpty());
92                 
93         List JavaDoc subGroup= new ArrayList JavaDoc();
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 JavaDoc(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 JavaDoc/*<Expression>*/ operands, ICompilationUnit cu) throws JavaModelException {
138         return Util.rangeIncludesNonWhitespaceOutsideRange(range, getRangeOfOperands(operands), cu.getBuffer());
139     }
140     
141     private static SourceRange getRangeOfOperands(List JavaDoc/*<Expression>*/ 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     /**
158      * Returns List of Lists of <code>ASTNode</code>s
159      */

160     private static List JavaDoc getMatchingContiguousNodeSubsequences(List JavaDoc source, List JavaDoc toMatch) {
161         //naive implementation:
162

163         List JavaDoc subsequences= new ArrayList JavaDoc();
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 JavaDoc subject, List JavaDoc 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 JavaDoc/*<Expression>*/ 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 JavaDoc myOperands= getOperands().iterator();
236         Iterator JavaDoc 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 JavaDoc 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 JavaDoc) 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 JavaDoc 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         //TODO: this would be a REAL union...:
297
// ArrayList union= new ArrayList();
298
// for (int i= 0; i < a1.length; i++) {
299
// union.add(a1[i]);
300
// }
301
// for (int i= 0; i < a2.length; i++) {
302
// if (! union.contains(a2[i]))
303
// union.add(a2[i]);
304
// }
305
// return (IASTFragment[]) union.toArray(new IASTFragment[union.size()]);
306
}
307
308
309     /**
310      * Note that this fragment does not directly
311      * represent this expression node, but rather
312      * a part of it.
313      */

314     public Expression getAssociatedExpression() {
315         return fGroupRoot;
316     }
317
318     /**
319      * Note that this fragment does not directly
320      * represent this node, but rather a particular sort of
321      * part of its subtree.
322      */

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 JavaDoc 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 JavaDoc 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 JavaDoc 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 JavaDoc source= cu.getBuffer().getText(getStartPosition(), getLength());
362         return (Expression) rewrite.createStringPlaceholder(source, ASTNode.INFIX_EXPRESSION);
363         
364 // //Todo: see whether we could copy bigger chunks of the original selection
365
// // (probably only possible from extendedOperands list or from nested InfixExpressions)
366
// InfixExpression result= rewrite.getAST().newInfixExpression();
367
// result.setOperator(getOperator());
368
// Expression first= (Expression) fOperands.get(0);
369
// Expression second= (Expression) fOperands.get(1);
370
// result.setLeftOperand((Expression) rewrite.createCopyTarget(first));
371
// result.setRightOperand((Expression) rewrite.createCopyTarget(second));
372
// for (int i= 2; i < fOperands.size(); i++) {
373
// Expression next= (Expression) fOperands.get(i);
374
// result.extendedOperands().add(rewrite.createCopyTarget(next));
375
// }
376
// return result;
377
}
378     
379     public void replace(ASTRewrite rewrite, ASTNode replacement, TextEditGroup textEditGroup) {
380         List JavaDoc allOperands= findGroupMembersInOrderFor(fGroupRoot);
381         if (allOperands.size() == fOperands.size()) {
382             rewrite.replace(fGroupRoot, replacement, textEditGroup);
383             return;
384         }
385         
386         // Could maybe be done with less edits.
387
// Problem is that the nodes to replace may not be all in the same InfixExpression.
388
int first= allOperands.indexOf(fOperands.get(0));
389         int after= first + fOperands.size();
390         ArrayList JavaDoc newOperands= new ArrayList JavaDoc();
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 /* i == first */ {
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 JavaDoc/*<Expression>*/ findGroupMembersInOrderFor(InfixExpression groupRoot) {
404         return new GroupMemberFinder(groupRoot).fMembersInOrder;
405     }
406
407     private static class GroupMemberFinder extends GenericVisitor {
408         private ArrayList JavaDoc/*<Expression>*/ fMembersInOrder= new ArrayList JavaDoc();
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 JavaDoc 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