001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.javaeditor.selectionactions;
011:
012: import java.util.List;
013:
014: import org.eclipse.core.runtime.Assert;
015:
016: import org.eclipse.jface.action.Action;
017: import org.eclipse.jface.dialogs.MessageDialog;
018:
019: import org.eclipse.jface.text.ITextSelection;
020:
021: import org.eclipse.jdt.core.IJavaElement;
022: import org.eclipse.jdt.core.ISourceRange;
023: import org.eclipse.jdt.core.ISourceReference;
024: import org.eclipse.jdt.core.ITypeRoot;
025: import org.eclipse.jdt.core.JavaModelException;
026: import org.eclipse.jdt.core.dom.ASTNode;
027: import org.eclipse.jdt.core.dom.CompilationUnit;
028: import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
029:
030: import org.eclipse.jdt.internal.corext.SourceRange;
031: import org.eclipse.jdt.internal.corext.dom.Selection;
032: import org.eclipse.jdt.internal.corext.dom.SelectionAnalyzer;
033:
034: import org.eclipse.jdt.ui.SharedASTProvider;
035:
036: import org.eclipse.jdt.internal.ui.JavaPlugin;
037: import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
038: import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
039:
040: public abstract class StructureSelectionAction extends Action {
041:
042: public static final String NEXT = "SelectNextElement"; //$NON-NLS-1$
043: public static final String PREVIOUS = "SelectPreviousElement"; //$NON-NLS-1$
044: public static final String ENCLOSING = "SelectEnclosingElement"; //$NON-NLS-1$
045: public static final String HISTORY = "RestoreLastSelection"; //$NON-NLS-1$
046:
047: private JavaEditor fEditor;
048: private SelectionHistory fSelectionHistory;
049:
050: protected StructureSelectionAction(String text, JavaEditor editor,
051: SelectionHistory history) {
052: super (text);
053: Assert.isNotNull(editor);
054: Assert.isNotNull(history);
055: fEditor = editor;
056: fSelectionHistory = history;
057: }
058:
059: /*
060: * This constructor is for testing purpose only.
061: */
062: protected StructureSelectionAction() {
063: super (""); //$NON-NLS-1$
064: }
065:
066: /*
067: * Method declared in IAction.
068: */
069: public final void run() {
070: IJavaElement inputElement = EditorUtility
071: .getEditorInputJavaElement(fEditor, false);
072: if (!(inputElement instanceof ITypeRoot && inputElement
073: .exists()))
074: return;
075:
076: ITypeRoot typeRoot = (ITypeRoot) inputElement;
077: ISourceRange sourceRange;
078: try {
079: sourceRange = typeRoot.getSourceRange();
080: if (sourceRange == null || sourceRange.getLength() == 0) {
081: MessageDialog
082: .openInformation(
083: fEditor.getEditorSite().getShell(),
084: SelectionActionMessages.StructureSelect_error_title,
085: SelectionActionMessages.StructureSelect_error_message);
086: return;
087: }
088: } catch (JavaModelException e) {
089: }
090: ITextSelection selection = getTextSelection();
091: ISourceRange newRange = getNewSelectionRange(
092: createSourceRange(selection), typeRoot);
093: // Check if new selection differs from current selection
094: if (selection.getOffset() == newRange.getOffset()
095: && selection.getLength() == newRange.getLength())
096: return;
097: fSelectionHistory.remember(new SourceRange(selection
098: .getOffset(), selection.getLength()));
099: try {
100: fSelectionHistory.ignoreSelectionChanges();
101: fEditor.selectAndReveal(newRange.getOffset(), newRange
102: .getLength());
103: } finally {
104: fSelectionHistory.listenToSelectionChanges();
105: }
106: }
107:
108: public final ISourceRange getNewSelectionRange(
109: ISourceRange oldSourceRange, ITypeRoot typeRoot) {
110: try {
111: CompilationUnit root = getAST(typeRoot);
112: if (root == null)
113: return oldSourceRange;
114: Selection selection = Selection.createFromStartLength(
115: oldSourceRange.getOffset(), oldSourceRange
116: .getLength());
117: SelectionAnalyzer selAnalyzer = new SelectionAnalyzer(
118: selection, true);
119: root.accept(selAnalyzer);
120: return internalGetNewSelectionRange(oldSourceRange,
121: typeRoot, selAnalyzer);
122: } catch (JavaModelException e) {
123: JavaPlugin.log(e); //dialog would be too heavy here
124: return new SourceRange(oldSourceRange.getOffset(),
125: oldSourceRange.getLength());
126: }
127: }
128:
129: /**
130: * Subclasses determine the actual new selection.
131: * @param oldSourceRange the selected range
132: * @param sr the current type root
133: * @param selAnalyzer the selection analyzer
134: * @return return the new selection range
135: * @throws JavaModelException
136: */
137: abstract ISourceRange internalGetNewSelectionRange(
138: ISourceRange oldSourceRange, ISourceReference sr,
139: SelectionAnalyzer selAnalyzer) throws JavaModelException;
140:
141: protected final ITextSelection getTextSelection() {
142: return (ITextSelection) fEditor.getSelectionProvider()
143: .getSelection();
144: }
145:
146: // -- helper methods for subclasses to fit a node range into the source range
147:
148: protected static ISourceRange getLastCoveringNodeRange(
149: ISourceRange oldSourceRange, ISourceReference sr,
150: SelectionAnalyzer selAnalyzer) throws JavaModelException {
151: if (selAnalyzer.getLastCoveringNode() == null)
152: return oldSourceRange;
153: else
154: return getSelectedNodeSourceRange(sr, selAnalyzer
155: .getLastCoveringNode());
156: }
157:
158: protected static ISourceRange getSelectedNodeSourceRange(
159: ISourceReference sr, ASTNode nodeToSelect)
160: throws JavaModelException {
161: int offset = nodeToSelect.getStartPosition();
162: int end = Math.min(sr.getSourceRange().getLength(),
163: nodeToSelect.getStartPosition()
164: + nodeToSelect.getLength() - 1);
165: return createSourceRange(offset, end);
166: }
167:
168: //-- private helper methods
169:
170: private static ISourceRange createSourceRange(ITextSelection ts) {
171: return new SourceRange(ts.getOffset(), ts.getLength());
172: }
173:
174: private static CompilationUnit getAST(ITypeRoot sr) {
175: return SharedASTProvider.getAST(sr, SharedASTProvider.WAIT_YES,
176: null);
177: }
178:
179: //-- helper methods for this class and subclasses
180:
181: static ISourceRange createSourceRange(int offset, int end) {
182: int length = end - offset + 1;
183: if (length == 0) //to allow 0-length selection
184: length = 1;
185: return new SourceRange(Math.max(0, offset), length);
186: }
187:
188: static ASTNode[] getSiblingNodes(ASTNode node) {
189: ASTNode parent = node.getParent();
190: StructuralPropertyDescriptor locationInParent = node
191: .getLocationInParent();
192: if (locationInParent.isChildListProperty()) {
193: List siblings = (List) parent
194: .getStructuralProperty(locationInParent);
195: return (ASTNode[]) siblings.toArray(new ASTNode[siblings
196: .size()]);
197: }
198: return null;
199: }
200:
201: static int findIndex(Object[] array, Object o) {
202: for (int i = 0; i < array.length; i++) {
203: Object object = array[i];
204: if (object == o)
205: return i;
206: }
207: return -1;
208: }
209:
210: }
|