1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.actions;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.ResourceBundle;
17 import net.sourceforge.phpdt.internal.corext.Assert;
19 import org.eclipse.jface.text.BadLocationException;
20 import org.eclipse.jface.text.BadPartitioningException;
21 import org.eclipse.jface.text.BadPositionCategoryException;
22 import org.eclipse.jface.text.DefaultPositionUpdater;
23 import org.eclipse.jface.text.DocumentEvent;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.IDocumentExtension3;
26 import org.eclipse.jface.text.IPositionUpdater;
27 import org.eclipse.jface.text.IRewriteTarget;
28 import org.eclipse.jface.text.ITextSelection;
29 import org.eclipse.jface.text.Position;
30 import org.eclipse.jface.viewers.ISelection;
31 import org.eclipse.jface.viewers.ISelectionProvider;
32 import org.eclipse.ui.IEditorInput;
33 import org.eclipse.ui.texteditor.IDocumentProvider;
34 import org.eclipse.ui.texteditor.ITextEditor;
35 import org.eclipse.ui.texteditor.ITextEditorExtension2;
36 import org.eclipse.ui.texteditor.TextEditorAction;
40 * Common block comment code.
44 public abstract class BlockCommentAction extends TextEditorAction {
47 * Creates a new instance.
52 public BlockCommentAction(ResourceBundle bundle, String prefix, ITextEditor editor) {
53 super(bundle, prefix, editor);
57 * An edit is a kind of <code>DocumentEvent</code>, in this case an edit instruction, that is
58 * affilitated with a <code>Position</code> on a document. The offset of the document event is
59 * not stored statically, but taken from the affiliated <code>Position</code>, which gets
60 * updated when other edits occurr.
62 static class Edit extends DocumentEvent {
65 * Factory for edits which manages the creation, installation and destruction of
66 * position categories, position updaters etc. on a certain document. Once a factory has
67 * been obtained, <code>Edit</code> objects can be obtained from it which will be linked to
68 * the document by positions of one position category.
69 * <p>Clients are required to call <code>release</code> once the <code>Edit</code>s are not
70 * used any more, so the positions can be discarded.</p>
72 public static class EditFactory {
74 /** The position category basename for this edits. */
75 private static final String CATEGORY= "__positionalEditPositionCategory"; //$NON-NLS-1$
77 /** The count of factories. */
78 private static int fgCount= 0;
80 /** This factory's category. */
81 private final String fCategory;
82 private IDocument fDocument;
83 private IPositionUpdater fUpdater;
86 * Creates a new <code>EditFactory</code> with an unambiguous position category name.
87 * @param document the document that is being edited.
89 public EditFactory(IDocument document) {
90 fCategory= CATEGORY + fgCount++;
95 * Creates a new edition on the document of this factory.
97 * @param offset the offset of the edition at the point when is created.
98 * @param length the length of the edition (not updated via the position update mechanism)
99 * @param text the text to be replaced on the document
100 * @return an <code>Edit</code> reflecting the edition on the document
102 public Edit createEdit(int offset, int length, String text) throws BadLocationException {
104 if (!fDocument.containsPositionCategory(fCategory)) {
105 fDocument.addPositionCategory(fCategory);
106 fUpdater= new DefaultPositionUpdater(fCategory);
107 fDocument.addPositionUpdater(fUpdater);
110 Position position= new Position(offset);
112 fDocument.addPosition(fCategory, position);
113 } catch (BadPositionCategoryException e) {
114 Assert.isTrue(false);
116 return new Edit(fDocument, length, text, position);
120 * Releases the position category on the document and uninstalls the position updater.
121 * <code>Edit</code>s managed by this factory are not updated after this call.
123 public void release() {
124 if (fDocument != null && fDocument.containsPositionCategory(fCategory)) {
125 fDocument.removePositionUpdater(fUpdater);
127 fDocument.removePositionCategory(fCategory);
128 } catch (BadPositionCategoryException e) {
129 Assert.isTrue(false);
137 /** The position in the document where this edit be executed. */
138 private Position fPosition;
141 * Creates a new edition on <code>document</code>, taking its offset from <code>position</code>.
143 * @param document the document being edited
144 * @param length the length of the edition
145 * @param text the replacement text of the edition
146 * @param position the position keeping the edition's offset
148 protected Edit(IDocument document, int length, String text, Position position) {
149 super(document, 0, length, text);
154 * @see org.eclipse.jface.text.DocumentEvent#getOffset()
156 public int getOffset() {
157 return fPosition.getOffset();
161 * Executes the edition on document. The offset is taken from the position.
163 * @throws BadLocationException if the execution of the document fails.
165 public void perform() throws BadLocationException {
166 getDocument().replace(getOffset(), getLength(), getText());
175 ITextEditor editor= getTextEditor();
176 if (editor == null || !ensureEditable(editor))
179 ITextSelection selection= getCurrentSelection();
180 if (!isValidSelection(selection))
183 if (!validateEditorInputState())
186 IDocumentProvider docProvider= editor.getDocumentProvider();
187 IEditorInput input= editor.getEditorInput();
188 if (docProvider == null || input == null)
191 IDocument document= docProvider.getDocument(input);
192 if (document == null)
195 IDocumentExtension3 docExtension;
196 if (document instanceof IDocumentExtension3)
197 docExtension= (IDocumentExtension3) document;
201 IRewriteTarget target= (IRewriteTarget)editor.getAdapter(IRewriteTarget.class);
202 if (target != null) {
203 target.beginCompoundChange();
206 Edit.EditFactory factory= new Edit.EditFactory(document);
209 runInternal(selection, docExtension, factory);
211 } catch (BadLocationException e) {
212 // can happen on concurrent modification, deletion etc. of the document
213 // -> don't complain, just bail out
214 } catch (BadPartitioningException e) {
216 Assert.isTrue(false, "bad partitioning"); //$NON-NLS-1$
220 if (target != null) {
221 target.endCompoundChange();
227 * Calls <code>perform</code> on all <code>Edit</code>s in <code>edits</code>.
229 * @param edits a list of <code>Edit</code>s
230 * @throws BadLocationException if an <code>Edit</code> threw such an exception.
232 protected void executeEdits(List edits) throws BadLocationException {
233 for (Iterator it= edits.iterator(); it.hasNext();) {
234 Edit edit= (Edit) it.next();
240 * Ensures that the editor is modifyable. If the editor is an instance of
241 * <code>ITextEditorExtension2</code>, its <code>validateEditorInputState</code> method
242 * is called, otherwise, the result of <code>isEditable</code> is returned.
244 * @param editor the editor to be checked
245 * @return <code>true</code> if the editor is editable, <code>false</code> otherwise
247 protected boolean ensureEditable(ITextEditor editor) {
248 Assert.isNotNull(editor);
250 if (editor instanceof ITextEditorExtension2) {
251 ITextEditorExtension2 ext= (ITextEditorExtension2) editor;
252 return ext.validateEditorInputState();
255 return editor.isEditable();
259 * @see org.eclipse.ui.texteditor.IUpdate#update()
261 public void update() {
265 if (!canModifyEditor() || !isValidSelection(getCurrentSelection()))
271 * Returns the editor's selection, or <code>null</code> if no selection can be obtained or the
272 * editor is <code>null</code>.
274 * @return the selection of the action's editor, or <code>null</code>
276 protected ITextSelection getCurrentSelection() {
277 ITextEditor editor= getTextEditor();
278 if (editor != null) {
279 ISelectionProvider provider= editor.getSelectionProvider();
280 if (provider != null) {
281 ISelection selection= provider.getSelection();
282 if (selection instanceof ITextSelection)
283 return (ITextSelection) selection;
290 * Runs the real command once all the editor, document, and selection checks have succeeded.
292 * @param selection the current selection we are being called for
293 * @param docExtension the document extension where we get the partitioning from
294 * @param factory the edit factory we can use to create <code>Edit</code>s
295 * @throws BadLocationException if an edition fails
296 * @throws BadPartitioningException if a partitioning call fails
298 protected abstract void runInternal(ITextSelection selection, IDocumentExtension3 docExtension, Edit.EditFactory factory) throws BadLocationException, BadPartitioningException;
301 * Checks whether <code>selection</code> is valid.
303 * @param selection the selection to check
304 * @return <code>true</code> if the selection is valid, <code>false</code> otherwise
306 protected abstract boolean isValidSelection(ITextSelection selection);
309 * Returns the text to be inserted at the selection start.
311 * @return the text to be inserted at the selection start
313 protected String getCommentStart() {
314 // for now: no space story
315 return "/*"; //$NON-NLS-1$
319 * Returns the text to be inserted at the selection end.
321 * @return the text to be inserted at the selection end
323 protected String getCommentEnd() {
324 // for now: no space story
325 return "*/"; //$NON-NLS-1$