KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > search > BreakContinueTargetFinder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 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.ui.search;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Collections JavaDoc;
16 import java.util.List JavaDoc;
17
18 import org.eclipse.jdt.core.IJavaElement;
19 import org.eclipse.jdt.core.ISourceReference;
20 import org.eclipse.jdt.core.JavaModelException;
21 import org.eclipse.jdt.core.ToolFactory;
22 import org.eclipse.jdt.core.compiler.IScanner;
23 import org.eclipse.jdt.core.compiler.InvalidInputException;
24 import org.eclipse.jdt.core.dom.AST;
25 import org.eclipse.jdt.core.dom.ASTNode;
26 import org.eclipse.jdt.core.dom.ASTVisitor;
27 import org.eclipse.jdt.core.dom.Block;
28 import org.eclipse.jdt.core.dom.BreakStatement;
29 import org.eclipse.jdt.core.dom.CompilationUnit;
30 import org.eclipse.jdt.core.dom.ContinueStatement;
31 import org.eclipse.jdt.core.dom.DoStatement;
32 import org.eclipse.jdt.core.dom.EnhancedForStatement;
33 import org.eclipse.jdt.core.dom.ForStatement;
34 import org.eclipse.jdt.core.dom.Initializer;
35 import org.eclipse.jdt.core.dom.LabeledStatement;
36 import org.eclipse.jdt.core.dom.MethodDeclaration;
37 import org.eclipse.jdt.core.dom.SimpleName;
38 import org.eclipse.jdt.core.dom.SwitchStatement;
39 import org.eclipse.jdt.core.dom.WhileStatement;
40
41 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
42 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
43
44 import org.eclipse.jdt.internal.ui.JavaPlugin;
45
46 /**
47  * Class used to find the target for a break or continue statement according
48  * to the language specification.
49  * <p>
50  * The target statement is a while, do, switch, for or a labeled statement.
51  * Break is described in section 14.15 of the JLS3 and continue in section 14.16.</p>
52  *
53  * @since 3.2
54  */

55 public class BreakContinueTargetFinder extends ASTVisitor {
56     private ASTNode fSelected;
57     private boolean fIsBreak;
58     private SimpleName fLabel;
59     private String JavaDoc fContents;//contents are used for scanning to select the right extent of the keyword
60
private static final Class JavaDoc[] STOPPERS= {MethodDeclaration.class, Initializer.class};
61     private static final Class JavaDoc[] BREAKTARGETS= {ForStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class, SwitchStatement.class};
62     private static final Class JavaDoc[] CONTINUETARGETS= {ForStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class};
63     private static final int BRACE_LENGTH= 1;
64
65     /*
66      * Initializes the finder. Returns error message or <code>null</code> if everything is OK.
67      */

68     public String JavaDoc initialize(CompilationUnit root, int offset, int length) {
69         return initialize(root, NodeFinder.perform(root, offset, length));
70     }
71     
72     /*
73      * Initializes the finder. Returns error message or <code>null</code> if everything is OK.
74      */

75     public String JavaDoc initialize(CompilationUnit root, ASTNode node) {
76         ASTNode controlNode= getBreakOrContinueNode(node);
77         if (controlNode != null){
78             fContents= getContents(root);
79             if (fContents == null)
80                 return SearchMessages.BreakContinueTargetFinder_cannot_highlight;
81
82             fSelected= controlNode;
83             fIsBreak= fSelected instanceof BreakStatement;
84             fLabel= getLabel();
85             return null;
86         } else {
87             return SearchMessages.BreakContinueTargetFinder_no_break_or_continue_selected;
88         }
89     }
90
91     /* Returns contents or <code>null</code> if there's trouble. */
92     private String JavaDoc getContents(CompilationUnit root) {
93         try {
94             IJavaElement rootElem= root.getJavaElement();
95             if ((rootElem instanceof ISourceReference))
96                 return ((ISourceReference)rootElem).getSource();
97             else
98                 return null;
99         } catch (JavaModelException e) {
100             //We must handle it here because JavaEditor does not expect an exception
101

102             /* showing a dialog here would be too heavy but we cannot just
103              * swallow the exception */

104             JavaPlugin.log(e);
105             return null;
106         }
107     }
108
109     //extract the control node: handle labels
110
private ASTNode getBreakOrContinueNode(ASTNode selectedNode) {
111         if (selectedNode instanceof BreakStatement)
112             return selectedNode;
113         if (selectedNode instanceof ContinueStatement)
114             return selectedNode;
115         if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof BreakStatement)
116             return selectedNode.getParent();
117         if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof ContinueStatement)
118             return selectedNode.getParent();
119         return null;
120     }
121
122     public List JavaDoc perform() {
123         return getNodesToHighlight();
124     }
125
126     private SimpleName getLabel() {
127         if (fIsBreak){
128             BreakStatement bs= (BreakStatement) fSelected;
129             return bs.getLabel();
130         } else {
131             ContinueStatement cs= (ContinueStatement) fSelected;
132             return cs.getLabel();
133         }
134     }
135     
136     private List JavaDoc getNodesToHighlight() {
137         ASTNode targetNode= findTargetNode(fSelected);
138         if (!isEnclosingStatement(targetNode))
139             return Collections.EMPTY_LIST;
140         
141         List JavaDoc list= new ArrayList JavaDoc();
142         ASTNode node= makeFakeNodeForFirstToken(targetNode);
143         if (node != null)
144             list.add(node);
145         
146         if (fIsBreak) {
147             node= makeFakeNodeForClosingBrace(targetNode);
148             if (node != null)
149                 list.add(node);
150         }
151         
152         return list;
153             
154     }
155
156     private boolean isEnclosingStatement(ASTNode targetNode) {
157         return (targetNode != null) && !(targetNode instanceof MethodDeclaration) && !(targetNode instanceof Initializer);
158     }
159
160     private ASTNode findTargetNode(ASTNode node) {
161         do {
162             node= node.getParent();
163         } while (keepWalkingUp(node));
164         return node;
165     }
166
167     private ASTNode makeFakeNodeForFirstToken(ASTNode node) {
168         try {
169             int length= getLengthOfFirstTokenOf(node);
170             if (length < 1)
171                 return node;//fallback
172
return makeFakeNode(node.getStartPosition(), length, node.getAST());
173         } catch (InvalidInputException e) {
174             return node;//fallback
175
}
176     }
177
178     private SimpleName makeFakeNode(int start, int length, AST ast) {
179         String JavaDoc fakeName= makeStringOfLength(length);
180         SimpleName name= ast.newSimpleName(fakeName);
181         name.setSourceRange(start, length);
182         return name;
183     }
184
185     private ASTNode makeFakeNodeForClosingBrace(ASTNode targetNode) {
186         ASTNode maybeBlock= getOptionalBlock(targetNode);
187         if (maybeBlock == null)
188             return null;
189         
190         /* Ideally, we'd scan backwards to find the '}' token, but it may be an overkill
191          * so I'll just assume the closing brace token has a fixed length. */

192         return makeFakeNode(ASTNodes.getExclusiveEnd(maybeBlock)-BRACE_LENGTH, BRACE_LENGTH, targetNode.getAST());
193     }
194
195     /*
196      * Block cannot be return type here because SwitchStatement has no block
197      * and yet it does have a closing brace.
198      */

199     private ASTNode getOptionalBlock(ASTNode targetNode) {
200         final ASTNode[] maybeBlock= new ASTNode[1];
201         targetNode.accept(new ASTVisitor(){
202             public boolean visit(ForStatement node) {
203                 if (node.getBody() instanceof Block)
204                     maybeBlock[0]= node.getBody();
205                 return false;
206             }
207             public boolean visit(EnhancedForStatement node) {
208                 if (node.getBody() instanceof Block)
209                     maybeBlock[0]= node.getBody();
210                 return false;
211             }
212             public boolean visit(WhileStatement node) {
213                 if (node.getBody() instanceof Block)
214                     maybeBlock[0]= node.getBody();
215                 return false;
216             }
217             public boolean visit(DoStatement node) {
218                 if (node.getBody() instanceof Block)
219                     maybeBlock[0]= node.getBody();
220                 return false;
221             }
222             public boolean visit(SwitchStatement node) {
223                 maybeBlock[0]= node;
224                 return false;
225             }
226         });
227         return maybeBlock[0];
228     }
229
230     private static String JavaDoc makeStringOfLength(int length) {
231         char[] chars= new char[length];
232         Arrays.fill(chars, 'x');
233         return new String JavaDoc(chars);
234     }
235
236     //must scan because of unicode
237
private int getLengthOfFirstTokenOf(ASTNode node) throws InvalidInputException {
238         IScanner scanner= ToolFactory.createScanner(true, true, false, true);
239         scanner.setSource(getSource(node).toCharArray());
240         scanner.getNextToken();
241         return scanner.getRawTokenSource().length;
242     }
243
244     private String JavaDoc getSource(ASTNode node) {
245         return fContents.substring(node.getStartPosition(), ASTNodes.getInclusiveEnd(node));
246     }
247
248     private boolean keepWalkingUp(ASTNode node) {
249         if (node == null)
250             return false;
251         if (isAnyInstanceOf(STOPPERS, node))
252             return false;
253         if (fLabel != null && LabeledStatement.class.isInstance(node)){
254             LabeledStatement ls= (LabeledStatement)node;
255             return ! areEqualLabels(ls.getLabel(), fLabel);
256         }
257         if (fLabel == null && fIsBreak && isAnyInstanceOf(BREAKTARGETS, node))
258             return false;
259         if (fLabel == null && !fIsBreak && isAnyInstanceOf(CONTINUETARGETS, node))
260             return false;
261         return true;
262     }
263
264     //TODO see bug 33739 - resolveBinding always returns null
265
//so we just compare names
266
private static boolean areEqualLabels(SimpleName labelToMatch, SimpleName labelSelected) {
267         return labelSelected.getIdentifier().equals(labelToMatch.getIdentifier());
268     }
269
270     private static boolean isAnyInstanceOf(Class JavaDoc[] continueTargets, ASTNode node) {
271         for (int i= 0; i < continueTargets.length; i++) {
272             if (continueTargets[i].isInstance(node))
273                 return true;
274         }
275         return false;
276     }
277 }
278
Popular Tags