1) Reintroduced PHPPerspectiveFactory (was lost with refactoring).
[phpeclipse.git] / net.sourceforge.phpeclipse.ui / src / net / sourceforge / phpdt / internal / ui / actions / BlockCommentAction.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2004 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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.ui.actions;
12
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.ResourceBundle;
16
17 //incastrix
18 //import org.eclipse.jface.text.Assert;
19 import org.eclipse.core.runtime.Assert;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.BadPartitioningException;
22 import org.eclipse.jface.text.BadPositionCategoryException;
23 import org.eclipse.jface.text.DefaultPositionUpdater;
24 import org.eclipse.jface.text.DocumentEvent;
25 import org.eclipse.jface.text.IDocument;
26 import org.eclipse.jface.text.IDocumentExtension3;
27 import org.eclipse.jface.text.IPositionUpdater;
28 import org.eclipse.jface.text.IRewriteTarget;
29 import org.eclipse.jface.text.ITextSelection;
30 import org.eclipse.jface.text.Position;
31 import org.eclipse.jface.viewers.ISelection;
32 import org.eclipse.jface.viewers.ISelectionProvider;
33 import org.eclipse.ui.IEditorInput;
34 import org.eclipse.ui.texteditor.IDocumentProvider;
35 import org.eclipse.ui.texteditor.ITextEditor;
36 import org.eclipse.ui.texteditor.ITextEditorExtension2;
37 import org.eclipse.ui.texteditor.TextEditorAction;
38
39 /**
40  * Common block comment code.
41  * 
42  * @since 3.0
43  */
44 public abstract class BlockCommentAction extends TextEditorAction {
45
46         /**
47          * Creates a new instance.
48          * 
49          * @param bundle
50          * @param prefix
51          * @param editor
52          */
53         public BlockCommentAction(ResourceBundle bundle, String prefix,
54                         ITextEditor editor) {
55                 super(bundle, prefix, editor);
56         }
57
58         /**
59          * An edit is a kind of <code>DocumentEvent</code>, in this case an edit
60          * instruction, that is affilitated with a <code>Position</code> on a
61          * document. The offset of the document event is not stored statically, but
62          * taken from the affiliated <code>Position</code>, which gets updated
63          * when other edits occurr.
64          */
65         static class Edit extends DocumentEvent {
66
67                 /**
68                  * Factory for edits which manages the creation, installation and
69                  * destruction of position categories, position updaters etc. on a
70                  * certain document. Once a factory has been obtained, <code>Edit</code>
71                  * objects can be obtained from it which will be linked to the document
72                  * by positions of one position category.
73                  * <p>
74                  * Clients are required to call <code>release</code> once the
75                  * <code>Edit</code>s are not used any more, so the positions can be
76                  * discarded.
77                  * </p>
78                  */
79                 public static class EditFactory {
80
81                         /** The position category basename for this edits. */
82                         private static final String CATEGORY = "__positionalEditPositionCategory"; //$NON-NLS-1$
83
84                         /** The count of factories. */
85                         private static int fgCount = 0;
86
87                         /** This factory's category. */
88                         private final String fCategory;
89
90                         private IDocument fDocument;
91
92                         private IPositionUpdater fUpdater;
93
94                         /**
95                          * Creates a new <code>EditFactory</code> with an unambiguous
96                          * position category name.
97                          * 
98                          * @param document
99                          *            the document that is being edited.
100                          */
101                         public EditFactory(IDocument document) {
102                                 fCategory = CATEGORY + fgCount++;
103                                 fDocument = document;
104                         }
105
106                         /**
107                          * Creates a new edition on the document of this factory.
108                          * 
109                          * @param offset
110                          *            the offset of the edition at the point when is
111                          *            created.
112                          * @param length
113                          *            the length of the edition (not updated via the
114                          *            position update mechanism)
115                          * @param text
116                          *            the text to be replaced on the document
117                          * @return an <code>Edit</code> reflecting the edition on the
118                          *         document
119                          */
120                         public Edit createEdit(int offset, int length, String text)
121                                         throws BadLocationException {
122
123                                 if (!fDocument.containsPositionCategory(fCategory)) {
124                                         fDocument.addPositionCategory(fCategory);
125                                         fUpdater = new DefaultPositionUpdater(fCategory);
126                                         fDocument.addPositionUpdater(fUpdater);
127                                 }
128
129                                 Position position = new Position(offset);
130                                 try {
131                                         fDocument.addPosition(fCategory, position);
132                                 } catch (BadPositionCategoryException e) {
133                                         Assert.isTrue(false);
134                                 }
135                                 return new Edit(fDocument, length, text, position);
136                         }
137
138                         /**
139                          * Releases the position category on the document and uninstalls the
140                          * position updater. <code>Edit</code>s managed by this factory
141                          * are not updated after this call.
142                          */
143                         public void release() {
144                                 if (fDocument != null
145                                                 && fDocument.containsPositionCategory(fCategory)) {
146                                         fDocument.removePositionUpdater(fUpdater);
147                                         try {
148                                                 fDocument.removePositionCategory(fCategory);
149                                         } catch (BadPositionCategoryException e) {
150                                                 Assert.isTrue(false);
151                                         }
152                                         fDocument = null;
153                                         fUpdater = null;
154                                 }
155                         }
156                 }
157
158                 /** The position in the document where this edit be executed. */
159                 private Position fPosition;
160
161                 /**
162                  * Creates a new edition on <code>document</code>, taking its offset
163                  * from <code>position</code>.
164                  * 
165                  * @param document
166                  *            the document being edited
167                  * @param length
168                  *            the length of the edition
169                  * @param text
170                  *            the replacement text of the edition
171                  * @param position
172                  *            the position keeping the edition's offset
173                  */
174                 protected Edit(IDocument document, int length, String text,
175                                 Position position) {
176                         super(document, 0, length, text);
177                         fPosition = position;
178                 }
179
180                 /*
181                  * @see org.eclipse.jface.text.DocumentEvent#getOffset()
182                  */
183                 public int getOffset() {
184                         return fPosition.getOffset();
185                 }
186
187                 /**
188                  * Executes the edition on document. The offset is taken from the
189                  * position.
190                  * 
191                  * @throws BadLocationException
192                  *             if the execution of the document fails.
193                  */
194                 public void perform() throws BadLocationException {
195                         getDocument().replace(getOffset(), getLength(), getText());
196                 }
197
198         }
199
200         public void run() {
201                 if (!isEnabled())
202                         return;
203
204                 ITextEditor editor = getTextEditor();
205                 if (editor == null || !ensureEditable(editor))
206                         return;
207
208                 ITextSelection selection = getCurrentSelection();
209                 if (!isValidSelection(selection))
210                         return;
211
212                 if (!validateEditorInputState())
213                         return;
214
215                 IDocumentProvider docProvider = editor.getDocumentProvider();
216                 IEditorInput input = editor.getEditorInput();
217                 if (docProvider == null || input == null)
218                         return;
219
220                 IDocument document = docProvider.getDocument(input);
221                 if (document == null)
222                         return;
223
224                 IDocumentExtension3 docExtension;
225                 if (document instanceof IDocumentExtension3)
226                         docExtension = (IDocumentExtension3) document;
227                 else
228                         return;
229
230                 IRewriteTarget target = (IRewriteTarget) editor
231                                 .getAdapter(IRewriteTarget.class);
232                 if (target != null) {
233                         target.beginCompoundChange();
234                 }
235
236                 Edit.EditFactory factory = new Edit.EditFactory(document);
237
238                 try {
239                         runInternal(selection, docExtension, factory);
240
241                 } catch (BadLocationException e) {
242                         // can happen on concurrent modification, deletion etc. of the
243                         // document
244                         // -> don't complain, just bail out
245                 } catch (BadPartitioningException e) {
246                         // should not happen
247                         Assert.isTrue(false, "bad partitioning"); //$NON-NLS-1$
248                 } finally {
249                         factory.release();
250
251                         if (target != null) {
252                                 target.endCompoundChange();
253                         }
254                 }
255         }
256
257         /**
258          * Calls <code>perform</code> on all <code>Edit</code>s in
259          * <code>edits</code>.
260          * 
261          * @param edits
262          *            a list of <code>Edit</code>s
263          * @throws BadLocationException
264          *             if an <code>Edit</code> threw such an exception.
265          */
266         protected void executeEdits(List edits) throws BadLocationException {
267                 for (Iterator it = edits.iterator(); it.hasNext();) {
268                         Edit edit = (Edit) it.next();
269                         edit.perform();
270                 }
271         }
272
273         /**
274          * Ensures that the editor is modifyable. If the editor is an instance of
275          * <code>ITextEditorExtension2</code>, its
276          * <code>validateEditorInputState</code> method is called, otherwise, the
277          * result of <code>isEditable</code> is returned.
278          * 
279          * @param editor
280          *            the editor to be checked
281          * @return <code>true</code> if the editor is editable, <code>false</code>
282          *         otherwise
283          */
284         protected boolean ensureEditable(ITextEditor editor) {
285                 Assert.isNotNull(editor);
286
287                 if (editor instanceof ITextEditorExtension2) {
288                         ITextEditorExtension2 ext = (ITextEditorExtension2) editor;
289                         return ext.validateEditorInputState();
290                 }
291
292                 return editor.isEditable();
293         }
294
295         /*
296          * @see org.eclipse.ui.texteditor.IUpdate#update()
297          */
298         public void update() {
299                 super.update();
300
301                 if (isEnabled()) {
302                         if (!canModifyEditor() || !isValidSelection(getCurrentSelection()))
303                                 setEnabled(false);
304                 }
305         }
306
307         /**
308          * Returns the editor's selection, or <code>null</code> if no selection
309          * can be obtained or the editor is <code>null</code>.
310          * 
311          * @return the selection of the action's editor, or <code>null</code>
312          */
313         protected ITextSelection getCurrentSelection() {
314                 ITextEditor editor = getTextEditor();
315                 if (editor != null) {
316                         ISelectionProvider provider = editor.getSelectionProvider();
317                         if (provider != null) {
318                                 ISelection selection = provider.getSelection();
319                                 if (selection instanceof ITextSelection)
320                                         return (ITextSelection) selection;
321                         }
322                 }
323                 return null;
324         }
325
326         /**
327          * Runs the real command once all the editor, document, and selection checks
328          * have succeeded.
329          * 
330          * @param selection
331          *            the current selection we are being called for
332          * @param docExtension
333          *            the document extension where we get the partitioning from
334          * @param factory
335          *            the edit factory we can use to create <code>Edit</code>s
336          * @throws BadLocationException
337          *             if an edition fails
338          * @throws BadPartitioningException
339          *             if a partitioning call fails
340          */
341         protected abstract void runInternal(ITextSelection selection,
342                         IDocumentExtension3 docExtension, Edit.EditFactory factory)
343                         throws BadLocationException, BadPartitioningException;
344
345         /**
346          * Checks whether <code>selection</code> is valid.
347          * 
348          * @param selection
349          *            the selection to check
350          * @return <code>true</code> if the selection is valid, <code>false</code>
351          *         otherwise
352          */
353         protected abstract boolean isValidSelection(ITextSelection selection);
354
355         /**
356          * Returns the text to be inserted at the selection start.
357          * 
358          * @return the text to be inserted at the selection start
359          */
360         protected String getCommentStart() {
361                 // for now: no space story
362                 return "/*"; //$NON-NLS-1$
363         }
364
365         /**
366          * Returns the text to be inserted at the selection end.
367          * 
368          * @return the text to be inserted at the selection end
369          */
370         protected String getCommentEnd() {
371                 // for now: no space story
372                 return "*/"; //$NON-NLS-1$
373         }
374
375 }