PageRenderTime 41ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/org.eclipse.wst.jsdt.ui/src/org/eclipse/wst/jsdt/internal/ui/search/BreakContinueTargetFinder.java

https://bitbucket.org/tomotaro1065/webtools.jsdt.core
Java | 281 lines | 213 code | 29 blank | 39 comment | 48 complexity | 363f59d256b224490a01f89cfd2922b1 MD5 | raw file
  1. /*******************************************************************************
  2. * Copyright (c) 2000, 2008 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.wst.jsdt.internal.ui.search;
  12. import java.util.ArrayList;
  13. import java.util.Arrays;
  14. import java.util.Collections;
  15. import java.util.List;
  16. import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  17. import org.eclipse.wst.jsdt.core.ISourceReference;
  18. import org.eclipse.wst.jsdt.core.JavaScriptModelException;
  19. import org.eclipse.wst.jsdt.core.ToolFactory;
  20. import org.eclipse.wst.jsdt.core.compiler.IScanner;
  21. import org.eclipse.wst.jsdt.core.compiler.InvalidInputException;
  22. import org.eclipse.wst.jsdt.core.dom.AST;
  23. import org.eclipse.wst.jsdt.core.dom.ASTNode;
  24. import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  25. import org.eclipse.wst.jsdt.core.dom.Block;
  26. import org.eclipse.wst.jsdt.core.dom.BreakStatement;
  27. import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  28. import org.eclipse.wst.jsdt.core.dom.ContinueStatement;
  29. import org.eclipse.wst.jsdt.core.dom.DoStatement;
  30. import org.eclipse.wst.jsdt.core.dom.EnhancedForStatement;
  31. import org.eclipse.wst.jsdt.core.dom.ForInStatement;
  32. import org.eclipse.wst.jsdt.core.dom.ForStatement;
  33. import org.eclipse.wst.jsdt.core.dom.Initializer;
  34. import org.eclipse.wst.jsdt.core.dom.LabeledStatement;
  35. import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  36. import org.eclipse.wst.jsdt.core.dom.SimpleName;
  37. import org.eclipse.wst.jsdt.core.dom.SwitchStatement;
  38. import org.eclipse.wst.jsdt.core.dom.WhileStatement;
  39. import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  40. import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  41. import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
  42. /**
  43. * Class used to find the target for a break or continue statement according
  44. * to the language specification.
  45. * <p>
  46. * The target statement is a while, do, switch, for or a labeled statement.
  47. * Break is described in section 14.15 of the JLS3 and continue in section 14.16.</p>
  48. *
  49. *
  50. */
  51. public class BreakContinueTargetFinder extends ASTVisitor {
  52. private ASTNode fSelected;
  53. private boolean fIsBreak;
  54. private SimpleName fLabel;
  55. private String fContents;//contents are used for scanning to select the right extent of the keyword
  56. private static final Class[] STOPPERS= {FunctionDeclaration.class, Initializer.class};
  57. private static final Class[] BREAKTARGETS= {ForStatement.class, ForInStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class, SwitchStatement.class};
  58. private static final Class[] CONTINUETARGETS= {ForStatement.class, ForInStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class};
  59. private static final int BRACE_LENGTH= 1;
  60. /*
  61. * Initializes the finder. Returns error message or <code>null</code> if everything is OK.
  62. */
  63. public String initialize(JavaScriptUnit root, int offset, int length) {
  64. return initialize(root, NodeFinder.perform(root, offset, length));
  65. }
  66. /*
  67. * Initializes the finder. Returns error message or <code>null</code> if everything is OK.
  68. */
  69. public String initialize(JavaScriptUnit root, ASTNode node) {
  70. ASTNode controlNode= getBreakOrContinueNode(node);
  71. if (controlNode != null){
  72. fContents= getContents(root);
  73. if (fContents == null)
  74. return SearchMessages.BreakContinueTargetFinder_cannot_highlight;
  75. fSelected= controlNode;
  76. fIsBreak= fSelected instanceof BreakStatement;
  77. fLabel= getLabel();
  78. return null;
  79. } else {
  80. return SearchMessages.BreakContinueTargetFinder_no_break_or_continue_selected;
  81. }
  82. }
  83. /* Returns contents or <code>null</code> if there's trouble. */
  84. private String getContents(JavaScriptUnit root) {
  85. try {
  86. IJavaScriptElement rootElem= root.getJavaElement();
  87. if ((rootElem instanceof ISourceReference))
  88. return ((ISourceReference)rootElem).getSource();
  89. else
  90. return null;
  91. } catch (JavaScriptModelException e) {
  92. //We must handle it here because JavaEditor does not expect an exception
  93. /* showing a dialog here would be too heavy but we cannot just
  94. * swallow the exception */
  95. JavaScriptPlugin.log(e);
  96. return null;
  97. }
  98. }
  99. //extract the control node: handle labels
  100. private ASTNode getBreakOrContinueNode(ASTNode selectedNode) {
  101. if (selectedNode instanceof BreakStatement)
  102. return selectedNode;
  103. if (selectedNode instanceof ContinueStatement)
  104. return selectedNode;
  105. if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof BreakStatement)
  106. return selectedNode.getParent();
  107. if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof ContinueStatement)
  108. return selectedNode.getParent();
  109. return null;
  110. }
  111. public List perform() {
  112. return getNodesToHighlight();
  113. }
  114. private SimpleName getLabel() {
  115. if (fIsBreak){
  116. BreakStatement bs= (BreakStatement) fSelected;
  117. return bs.getLabel();
  118. } else {
  119. ContinueStatement cs= (ContinueStatement) fSelected;
  120. return cs.getLabel();
  121. }
  122. }
  123. private List getNodesToHighlight() {
  124. ASTNode targetNode= findTargetNode(fSelected);
  125. if (!isEnclosingStatement(targetNode))
  126. return Collections.EMPTY_LIST;
  127. List list= new ArrayList();
  128. ASTNode node= makeFakeNodeForFirstToken(targetNode);
  129. if (node != null)
  130. list.add(node);
  131. if (fIsBreak) {
  132. node= makeFakeNodeForClosingBrace(targetNode);
  133. if (node != null)
  134. list.add(node);
  135. }
  136. return list;
  137. }
  138. private boolean isEnclosingStatement(ASTNode targetNode) {
  139. return (targetNode != null) && !(targetNode instanceof FunctionDeclaration) && !(targetNode instanceof Initializer);
  140. }
  141. private ASTNode findTargetNode(ASTNode node) {
  142. do {
  143. node= node.getParent();
  144. } while (keepWalkingUp(node));
  145. return node;
  146. }
  147. private ASTNode makeFakeNodeForFirstToken(ASTNode node) {
  148. try {
  149. int length= getLengthOfFirstTokenOf(node);
  150. if (length < 1)
  151. return node;//fallback
  152. return makeFakeNode(node.getStartPosition(), length, node.getAST());
  153. } catch (InvalidInputException e) {
  154. return node;//fallback
  155. }
  156. }
  157. private SimpleName makeFakeNode(int start, int length, AST ast) {
  158. String fakeName= makeStringOfLength(length);
  159. SimpleName name= ast.newSimpleName(fakeName);
  160. name.setSourceRange(start, length);
  161. return name;
  162. }
  163. private ASTNode makeFakeNodeForClosingBrace(ASTNode targetNode) {
  164. ASTNode maybeBlock= getOptionalBlock(targetNode);
  165. if (maybeBlock == null)
  166. return null;
  167. /* Ideally, we'd scan backwards to find the '}' token, but it may be an overkill
  168. * so I'll just assume the closing brace token has a fixed length. */
  169. return makeFakeNode(ASTNodes.getExclusiveEnd(maybeBlock)-BRACE_LENGTH, BRACE_LENGTH, targetNode.getAST());
  170. }
  171. /*
  172. * Block cannot be return type here because SwitchStatement has no block
  173. * and yet it does have a closing brace.
  174. */
  175. private ASTNode getOptionalBlock(ASTNode targetNode) {
  176. final ASTNode[] maybeBlock= new ASTNode[1];
  177. targetNode.accept(new ASTVisitor(){
  178. public boolean visit(ForStatement node) {
  179. if (node.getBody() instanceof Block)
  180. maybeBlock[0]= node.getBody();
  181. return false;
  182. }
  183. public boolean visit(ForInStatement node) {
  184. if (node.getBody() instanceof Block)
  185. maybeBlock[0]= node.getBody();
  186. return false;
  187. }
  188. public boolean visit(EnhancedForStatement node) {
  189. if (node.getBody() instanceof Block)
  190. maybeBlock[0]= node.getBody();
  191. return false;
  192. }
  193. public boolean visit(WhileStatement node) {
  194. if (node.getBody() instanceof Block)
  195. maybeBlock[0]= node.getBody();
  196. return false;
  197. }
  198. public boolean visit(DoStatement node) {
  199. if (node.getBody() instanceof Block)
  200. maybeBlock[0]= node.getBody();
  201. return false;
  202. }
  203. public boolean visit(SwitchStatement node) {
  204. maybeBlock[0]= node;
  205. return false;
  206. }
  207. });
  208. return maybeBlock[0];
  209. }
  210. private static String makeStringOfLength(int length) {
  211. char[] chars= new char[length];
  212. Arrays.fill(chars, 'x');
  213. return new String(chars);
  214. }
  215. //must scan because of unicode
  216. private int getLengthOfFirstTokenOf(ASTNode node) throws InvalidInputException {
  217. IScanner scanner= ToolFactory.createScanner(true, true, false, true);
  218. scanner.setSource(getSource(node).toCharArray());
  219. scanner.getNextToken();
  220. return scanner.getRawTokenSource().length;
  221. }
  222. private String getSource(ASTNode node) {
  223. return fContents.substring(node.getStartPosition(), ASTNodes.getInclusiveEnd(node));
  224. }
  225. private boolean keepWalkingUp(ASTNode node) {
  226. if (node == null)
  227. return false;
  228. if (isAnyInstanceOf(STOPPERS, node))
  229. return false;
  230. if (fLabel != null && LabeledStatement.class.isInstance(node)){
  231. LabeledStatement ls= (LabeledStatement)node;
  232. return ! areEqualLabels(ls.getLabel(), fLabel);
  233. }
  234. if (fLabel == null && fIsBreak && isAnyInstanceOf(BREAKTARGETS, node))
  235. return false;
  236. if (fLabel == null && !fIsBreak && isAnyInstanceOf(CONTINUETARGETS, node))
  237. return false;
  238. return true;
  239. }
  240. //TODO see bug 33739 - resolveBinding always returns null
  241. //so we just compare names
  242. private static boolean areEqualLabels(SimpleName labelToMatch, SimpleName labelSelected) {
  243. return labelSelected.getIdentifier().equals(labelToMatch.getIdentifier());
  244. }
  245. private static boolean isAnyInstanceOf(Class[] continueTargets, ASTNode node) {
  246. for (int i= 0; i < continueTargets.length; i++) {
  247. if (continueTargets[i].isInstance(node))
  248. return true;
  249. }
  250. return false;
  251. }
  252. }