Index: ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java,v
retrieving revision 1.365
diff -u -r1.365 JavaEditor.java
--- ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java 14 Jun 2005 16:20:33 -0000 1.365
+++ ui/org/eclipse/jdt/internal/ui/javaeditor/JavaEditor.java 5 Oct 2005 14:43:51 -0000
@@ -208,6 +208,7 @@
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectNextAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectPreviousAction;
import org.eclipse.jdt.internal.ui.javaeditor.selectionactions.StructureSelectionAction;
+import org.eclipse.jdt.internal.ui.search.BreakContinueTargetFinder;
import org.eclipse.jdt.internal.ui.search.ExceptionOccurrencesFinder;
import org.eclipse.jdt.internal.ui.search.ImplementOccurrencesFinder;
import org.eclipse.jdt.internal.ui.search.MethodExitsFinder;
@@ -1555,6 +1556,14 @@
* @since 3.0
*/
private boolean fMarkMethodExitPoints;
+
+ /**
+ * Tells whether to mark targets of break
and continue
statements in this editor.
+ * Only valid if {@link #fMarkOccurrenceAnnotations} is true
.
+ * @since 3.2
+ */
+ private boolean fMarkBreakContinueTargets;
+
/**
* Tells whether to mark implementors in this editor.
* Only valid if {@link #fMarkOccurrenceAnnotations} is true
.
@@ -1673,6 +1682,7 @@
fMarkExceptions= store.getBoolean(PreferenceConstants.EDITOR_MARK_EXCEPTION_OCCURRENCES);
fMarkImplementors= store.getBoolean(PreferenceConstants.EDITOR_MARK_IMPLEMENTORS);
fMarkMethodExitPoints= store.getBoolean(PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS);
+ fMarkBreakContinueTargets= store.getBoolean(PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS);
}
/*
@@ -2471,6 +2481,10 @@
fMarkMethodExitPoints= newBooleanValue;
return;
}
+ if (PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS.equals(property)) {
+ fMarkBreakContinueTargets= newBooleanValue;
+ return;
+ }
if (PreferenceConstants.EDITOR_MARK_IMPLEMENTORS.equals(property)) {
fMarkImplementors= newBooleanValue;
return;
@@ -2929,6 +2943,16 @@
}
}
+ if ((matches == null || matches.isEmpty()) && (fMarkBreakContinueTargets || fMarkTypeOccurrences)) {
+ BreakContinueTargetFinder finder= new BreakContinueTargetFinder();
+ String message= finder.initialize(astRoot, selection.getOffset(), selection.getLength());
+ if (message == null) {
+ matches= finder.perform();
+ if (!fMarkBreakContinueTargets && !matches.isEmpty())
+ matches.clear();
+ }
+ }
+
if ((matches == null || matches.isEmpty()) && (fMarkImplementors || fMarkTypeOccurrences)) {
ImplementOccurrencesFinder finder= new ImplementOccurrencesFinder();
String message= finder.initialize(astRoot, selection.getOffset(), selection.getLength());
Index: ui/org/eclipse/jdt/internal/ui/search/SearchMessages.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/search/SearchMessages.java,v
retrieving revision 1.10
diff -u -r1.10 SearchMessages.java
--- ui/org/eclipse/jdt/internal/ui/search/SearchMessages.java 19 Apr 2005 10:45:48 -0000 1.10
+++ ui/org/eclipse/jdt/internal/ui/search/SearchMessages.java 5 Oct 2005 14:43:51 -0000
@@ -193,6 +193,8 @@
public static String MatchFilter_InexactFilter_actionLabel;
public static String MatchFilter_InexactFilter_description;
public static String MethodExitsFinder_no_return_type_selected;
+ public static String BreakContinueFinder_cannot_hightlight;
+ public static String BreakContinueFinder_no_break_or_continue_selected;
public static String TextSearchLabelProvider_matchCountFormat;
public static String FiltersDialog_title;
public static String FiltersDialog_filters_label;
Index: ui/org/eclipse/jdt/internal/ui/search/SearchMessages.properties
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/search/SearchMessages.properties,v
retrieving revision 1.103
diff -u -r1.103 SearchMessages.properties
--- ui/org/eclipse/jdt/internal/ui/search/SearchMessages.properties 13 Apr 2005 17:35:12 -0000 1.103
+++ ui/org/eclipse/jdt/internal/ui/search/SearchMessages.properties 5 Oct 2005 14:43:51 -0000
@@ -248,6 +248,9 @@
MethodExitsFinder_no_return_type_selected=No return type selected
+BreakContinueFinder_cannot_hightlight=Occurrences in this element cannot be highlighted
+BreakContinueFinder_no_break_or_continue_selected=No break or continue selected
+
TextSearchLabelProvider_matchCountFormat={0} ({1} matches)
FiltersDialog_title=Java Search Filters
Index: ui/org/eclipse/jdt/ui/PreferenceConstants.java
===================================================================
RCS file: /cvsroot/eclipse/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/PreferenceConstants.java,v
retrieving revision 1.168
diff -u -r1.168 PreferenceConstants.java
--- ui/org/eclipse/jdt/ui/PreferenceConstants.java 27 May 2005 08:04:13 -0000 1.168
+++ ui/org/eclipse/jdt/ui/PreferenceConstants.java 5 Oct 2005 14:43:51 -0000
@@ -2222,6 +2222,17 @@
public static final String EDITOR_MARK_METHOD_EXIT_POINTS= "markMethodExitPoints"; //$NON-NLS-1$
/**
+ * A named preference that controls whether targets for of break
and continue
statements are marked.
+ * Only valid if {@link #EDITOR_MARK_OCCURRENCES} is true
.
+ *
+ * Value is of type Boolean
.
+ *
true
.
* @@ -3475,6 +3486,7 @@ store.setDefault(PreferenceConstants.EDITOR_MARK_LOCAL_VARIABLE_OCCURRENCES, true); store.setDefault(PreferenceConstants.EDITOR_MARK_EXCEPTION_OCCURRENCES, true); store.setDefault(PreferenceConstants.EDITOR_MARK_METHOD_EXIT_POINTS, true); + store.setDefault(PreferenceConstants.EDITOR_MARK_BREAK_CONTINUE_TARGETS, true); store.setDefault(PreferenceConstants.EDITOR_MARK_IMPLEMENTORS, true); // spell checking Index: ui/org/eclipse/jdt/internal/ui/search/BreakContinueTargetFinder.java =================================================================== RCS file: ui/org/eclipse/jdt/internal/ui/search/BreakContinueTargetFinder.java diff -N ui/org/eclipse/jdt/internal/ui/search/BreakContinueTargetFinder.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ ui/org/eclipse/jdt/internal/ui/search/BreakContinueTargetFinder.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.ui.search; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.ISourceReference; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.ToolFactory; +import org.eclipse.jdt.core.compiler.IScanner; +import org.eclipse.jdt.core.compiler.InvalidInputException; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.BreakStatement; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.ContinueStatement; +import org.eclipse.jdt.core.dom.DoStatement; +import org.eclipse.jdt.core.dom.EnhancedForStatement; +import org.eclipse.jdt.core.dom.ForStatement; +import org.eclipse.jdt.core.dom.Initializer; +import org.eclipse.jdt.core.dom.LabeledStatement; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.SwitchStatement; +import org.eclipse.jdt.core.dom.WhileStatement; + +import org.eclipse.jdt.internal.corext.dom.ASTNodes; +import org.eclipse.jdt.internal.corext.dom.NodeFinder; + +import org.eclipse.jdt.internal.ui.JavaPlugin; + +/** + * Class used to find the target for a break or continue statement according + * to the language spec. + * The target statement is a while, do, switch, for or a labeled statement. + * Break is described in section 14.15 of the JLS3 and continue in section 14.16. + */ +public class BreakContinueTargetFinder extends ASTVisitor { + private ASTNode fSelected; + private List fResult; + private boolean fIsBreak; + private SimpleName fLabel; + private String fContents;//contents are used for scanning to select the right extent of the keyword + private static final Class[] STOPPERS = {MethodDeclaration.class, Initializer.class}; + private static final Class[] BREAKTARGETS = {ForStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class, SwitchStatement.class}; + private static final Class[] CONTINUETARGETS = {ForStatement.class, EnhancedForStatement.class, WhileStatement.class, DoStatement.class}; + + public String initialize(CompilationUnit root, int offset, int length) { + ASTNode controlNode= getBreakOrContinueNode(NodeFinder.perform(root, offset, length)); + if (controlNode != null){ + fSelected= controlNode; + fIsBreak= fSelected instanceof BreakStatement; + fContents= getContents(root); + if (fContents == null) + return SearchMessages.BreakContinueFinder_cannot_hightlight; + else + return null; + } else { + return SearchMessages.BreakContinueFinder_no_break_or_continue_selected; + } + } + + /* Returns contents or null if there's trouble. */ + private String getContents(CompilationUnit root) { + try { + IJavaElement rootElem = root.getJavaElement(); + if ((rootElem instanceof ISourceReference)) + return ((ISourceReference)rootElem).getSource(); + else + return null; + } catch (JavaModelException e) { + //We must handle it here because JavaEditor does not expect an exception + + /* showing a dialog here would be too heavy but we cannot just + * swallow the exception */ + JavaPlugin.log(e); + return null; + } + } + + //extract the control node: handle labels + private ASTNode getBreakOrContinueNode(ASTNode selectedNode) { + if (selectedNode instanceof BreakStatement) + return selectedNode; + if (selectedNode instanceof ContinueStatement) + return selectedNode; + if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof BreakStatement) + return selectedNode.getParent(); + if (selectedNode instanceof SimpleName && selectedNode.getParent() instanceof ContinueStatement) + return selectedNode.getParent(); + return null; + } + + public List perform() { + fResult= new ArrayList(1); + fLabel= getLabel(); + markReferences(); + return fResult; + } + + private SimpleName getLabel() { + if (fIsBreak){ + BreakStatement bs = (BreakStatement) fSelected; + return bs.getLabel(); + } else { + ContinueStatement cs= (ContinueStatement) fSelected; + return cs.getLabel(); + } + } + + private void markReferences() { + ASTNode node= getTarget(fSelected); + if (node != null) + fResult.add(node); + } + + private ASTNode getTarget(ASTNode node) { + do { + node= node.getParent(); + } while (keepWalkingUp(node)); + boolean noEnclosingStatement = (node == null) + || (node instanceof MethodDeclaration) + || (node instanceof Initializer); + return noEnclosingStatement ? null : makeFakeNodeForFirstToken(node); + } + + private ASTNode makeFakeNodeForFirstToken(ASTNode node) { + try { + int length = getLengthOfFirstTokenOf(node); + if (length < 1) + return node;//fallback + String fakeName = makeStringOfLength(length); + SimpleName name= node.getAST().newSimpleName(fakeName); + name.setSourceRange(node.getStartPosition(), length); + return name; + } catch (InvalidInputException e) { + return node;//fallback + } + } + + private static String makeStringOfLength(int length) { + char[] chars= new char[length]; + Arrays.fill(chars, 'x'); + return new String(chars); + } + + //must scan because of unicode + private int getLengthOfFirstTokenOf(ASTNode node) throws InvalidInputException { + IScanner scanner= ToolFactory.createScanner(true, true, false, true); + scanner.setSource(getSource(node).toCharArray()); + scanner.getNextToken(); + return scanner.getRawTokenSource().length; + } + + private String getSource(ASTNode node) { + return fContents.substring(node.getStartPosition(), ASTNodes.getInclusiveEnd(node)); + } + + private boolean keepWalkingUp(ASTNode node) { + if (node == null) + return false; + if (isAnyInstanceOf(STOPPERS, node)) + return false; + if (fLabel != null && LabeledStatement.class.isInstance(node)){ + LabeledStatement ls= (LabeledStatement)node; + return ! areEqualLabels(ls.getLabel(), fLabel); + } + if (fLabel == null && fIsBreak && isAnyInstanceOf(BREAKTARGETS, node)) + return false; + if (fLabel == null && !fIsBreak && isAnyInstanceOf(CONTINUETARGETS, node)) + return false; + return true; + } + + //TODO see bug 33739 - resolveBinding always returns null + //so we just compare names + private static boolean areEqualLabels(SimpleName labelToMatch, SimpleName labelSelected) { + return labelSelected.getIdentifier().equals(labelToMatch.getIdentifier()); + } + + private static boolean isAnyInstanceOf(Class[] continueTargets, ASTNode node) { + for (int i = 0; i < continueTargets.length; i++) { + if (continueTargets[i].isInstance(node)) + return true; + } + return false; + } +}