KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > DocumentCommand


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 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
12 package org.eclipse.jface.text;
13
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Collections JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.ListIterator JavaDoc;
20 import java.util.NoSuchElementException JavaDoc;
21
22 import org.eclipse.swt.events.VerifyEvent;
23
24 import org.eclipse.core.runtime.Assert;
25
26
27 /**
28  * Represents a text modification as a document replace command. The text
29  * modification is given as a {@link org.eclipse.swt.events.VerifyEvent} and
30  * translated into a document replace command relative to a given offset. A
31  * document command can also be used to initialize a given
32  * <code>VerifyEvent</code>.
33  * <p>
34  * A document command can also represent a list of related changes.</p>
35  */

36 public class DocumentCommand {
37
38     /**
39      * A command which is added to document commands.
40      * @since 2.1
41      */

42     private static class Command implements Comparable JavaDoc {
43         /** The offset of the range to be replaced */
44         private final int fOffset;
45         /** The length of the range to be replaced. */
46         private final int fLength;
47         /** The replacement text */
48         private final String JavaDoc fText;
49         /** The listener who owns this command */
50         private final IDocumentListener fOwner;
51
52         /**
53          * Creates a new command with the given specification.
54          *
55          * @param offset the offset of the replace command
56          * @param length the length of the replace command
57          * @param text the text to replace with, may be <code>null</code>
58          * @param owner the document command owner, may be <code>null</code>
59          * @since 3.0
60          */

61         public Command(int offset, int length, String JavaDoc text, IDocumentListener owner) {
62             if (offset < 0 || length < 0)
63                 throw new IllegalArgumentException JavaDoc();
64             fOffset= offset;
65             fLength= length;
66             fText= text;
67             fOwner= owner;
68         }
69
70         /**
71          * Returns the length delta for this command.
72          *
73          * @return the length delta for this command
74          */

75         public int getDeltaLength() {
76             return (fText == null ? 0 : fText.length()) - fLength;
77         }
78
79         /**
80          * Executes the document command on the specified document.
81          *
82          * @param document the document on which to execute the command.
83          * @throws BadLocationException in case this commands cannot be executed
84          */

85         public void execute(IDocument document) throws BadLocationException {
86
87             if (fLength == 0 && fText == null)
88                 return;
89
90             if (fOwner != null)
91                 document.removeDocumentListener(fOwner);
92
93             document.replace(fOffset, fLength, fText);
94
95             if (fOwner != null)
96                 document.addDocumentListener(fOwner);
97         }
98
99         /*
100          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
101          */

102         public int compareTo(final Object JavaDoc object) {
103             if (isEqual(object))
104                 return 0;
105
106             final Command command= (Command) object;
107
108             // diff middle points if not intersecting
109
if (fOffset + fLength <= command.fOffset || command.fOffset + command.fLength <= fOffset) {
110                 int value= (2 * fOffset + fLength) - (2 * command.fOffset + command.fLength);
111                 if (value != 0)
112                     return value;
113             }
114             // the answer
115
return 42;
116         }
117
118         private boolean isEqual(Object JavaDoc object) {
119             if (object == this)
120                 return true;
121             if (! (object instanceof Command))
122                 return false;
123             final Command command= (Command) object;
124             return command.fOffset == fOffset && command.fLength == fLength;
125         }
126     }
127
128     /**
129      * An iterator, which iterates in reverse over a list.
130      */

131     private static class ReverseListIterator implements Iterator JavaDoc {
132
133         /** The list iterator. */
134         private final ListIterator JavaDoc fListIterator;
135
136         /**
137          * Creates a reverse list iterator.
138          * @param listIterator the iterator that this reverse iterator is based upon
139          */

140         public ReverseListIterator(ListIterator JavaDoc listIterator) {
141             if (listIterator == null)
142                 throw new IllegalArgumentException JavaDoc();
143             fListIterator= listIterator;
144         }
145
146         /*
147          * @see java.util.Iterator#hasNext()
148          */

149         public boolean hasNext() {
150             return fListIterator.hasPrevious();
151         }
152
153         /*
154          * @see java.util.Iterator#next()
155          */

156         public Object JavaDoc next() {
157             return fListIterator.previous();
158         }
159
160         /*
161          * @see java.util.Iterator#remove()
162          */

163         public void remove() {
164             throw new UnsupportedOperationException JavaDoc();
165         }
166     }
167
168     /**
169      * A command iterator.
170      */

171     private static class CommandIterator implements Iterator JavaDoc {
172
173         /** The command iterator. */
174         private final Iterator JavaDoc fIterator;
175
176         /** The original command. */
177         private Command fCommand;
178
179         /** A flag indicating the direction of iteration. */
180         private boolean fForward;
181
182         /**
183          * Creates a command iterator.
184          *
185          * @param commands an ascending ordered list of commands
186          * @param command the original command
187          * @param forward the direction
188          */

189         public CommandIterator(final List JavaDoc commands, final Command command, final boolean forward) {
190             if (commands == null || command == null)
191                 throw new IllegalArgumentException JavaDoc();
192             fIterator= forward ? commands.iterator() : new ReverseListIterator(commands.listIterator(commands.size()));
193             fCommand= command;
194             fForward= forward;
195         }
196
197         /*
198          * @see java.util.Iterator#hasNext()
199          */

200         public boolean hasNext() {
201             return fCommand != null || fIterator.hasNext();
202         }
203
204         /*
205          * @see java.util.Iterator#next()
206          */

207         public Object JavaDoc next() {
208
209             if (! hasNext())
210                 throw new NoSuchElementException JavaDoc();
211
212             if (fCommand == null)
213                 return fIterator.next();
214
215             if (!fIterator.hasNext()) {
216                 final Command tempCommand= fCommand;
217                 fCommand= null;
218                 return tempCommand;
219             }
220
221             final Command command= (Command) fIterator.next();
222             final int compareValue= command.compareTo(fCommand);
223
224             if ((compareValue < 0) ^ ! fForward) {
225                 return command;
226
227             } else if ((compareValue > 0) ^ ! fForward) {
228                 final Command tempCommand= fCommand;
229                 fCommand= command;
230                 return tempCommand;
231
232             } else {
233                 throw new IllegalArgumentException JavaDoc();
234             }
235         }
236
237         /*
238          * @see java.util.Iterator#remove()
239          */

240         public void remove() {
241             throw new UnsupportedOperationException JavaDoc();
242         }
243     }
244
245     /** Must the command be updated */
246     public boolean doit= false;
247     /** The offset of the command. */
248     public int offset;
249     /** The length of the command */
250     public int length;
251     /** The text to be inserted */
252     public String JavaDoc text;
253     /**
254      * The owner of the document command which will not be notified.
255      * @since 2.1
256      */

257     public IDocumentListener owner;
258     /**
259      * The caret offset with respect to the document before the document command is executed.
260      * @since 2.1
261      */

262     public int caretOffset;
263     /**
264      * Additional document commands.
265      * @since 2.1
266      */

267     private final List JavaDoc fCommands= new ArrayList JavaDoc();
268     /**
269      * Indicates whether the caret should be shifted by this command.
270      * @since 3.0
271      */

272     public boolean shiftsCaret;
273
274
275     /**
276      * Creates a new document command.
277      */

278     protected DocumentCommand() {
279     }
280
281     /**
282      * Translates a verify event into a document replace command using the given offset.
283      *
284      * @param event the event to be translated
285      * @param modelRange the event range as model range
286      */

287     void setEvent(VerifyEvent event, IRegion modelRange) {
288
289         doit= true;
290         text= event.text;
291
292         offset= modelRange.getOffset();
293         length= modelRange.getLength();
294
295         owner= null;
296         caretOffset= -1;
297         shiftsCaret= true;
298         fCommands.clear();
299     }
300
301     /**
302      * Fills the given verify event with the replace text and the <code>doit</code>
303      * flag of this document command. Returns whether the document command
304      * covers the same range as the verify event considering the given offset.
305      *
306      * @param event the event to be changed
307      * @param modelRange to be considered for range comparison
308      * @return <code>true</code> if this command and the event cover the same range
309      */

310     boolean fillEvent(VerifyEvent event, IRegion modelRange) {
311         event.text= text;
312         event.doit= (offset == modelRange.getOffset() && length == modelRange.getLength() && doit && caretOffset == -1);
313         return event.doit;
314     }
315
316     /**
317      * Adds an additional replace command. The added replace command must not overlap
318      * with existing ones. If the document command owner is not <code>null</code>, it will not
319      * get document change notifications for the particular command.
320      *
321      * @param commandOffset the offset of the region to replace
322      * @param commandLength the length of the region to replace
323      * @param commandText the text to replace with, may be <code>null</code>
324      * @param commandOwner the command owner, may be <code>null</code>
325      * @throws BadLocationException if the added command intersects with an existing one
326      * @since 2.1
327      */

328     public void addCommand(int commandOffset, int commandLength, String JavaDoc commandText, IDocumentListener commandOwner) throws BadLocationException {
329         final Command command= new Command(commandOffset, commandLength, commandText, commandOwner);
330
331         if (intersects(command))
332             throw new BadLocationException();
333
334         final int index= Collections.binarySearch(fCommands, command);
335
336         // a command with exactly the same ranges exists already
337
if (index >= 0)
338             throw new BadLocationException();
339
340         // binary search result is defined as (-(insertionIndex) - 1)
341
final int insertionIndex= -(index + 1);
342
343         // overlaps to the right?
344
if (insertionIndex != fCommands.size() && intersects((Command) fCommands.get(insertionIndex), command))
345             throw new BadLocationException();
346
347         // overlaps to the left?
348
if (insertionIndex != 0 && intersects((Command) fCommands.get(insertionIndex - 1), command))
349             throw new BadLocationException();
350
351         fCommands.add(insertionIndex, command);
352     }
353
354     /**
355      * Returns an iterator over the commands in ascending position order.
356      * The iterator includes the original document command.
357      * Commands cannot be removed.
358      *
359      * @return returns the command iterator
360      */

361     public Iterator JavaDoc getCommandIterator() {
362         Command command= new Command(offset, length, text, owner);
363         return new CommandIterator(fCommands, command, true);
364     }
365
366     /**
367      * Returns the number of commands including the original document command.
368      *
369      * @return returns the number of commands
370      * @since 2.1
371      */

372     public int getCommandCount() {
373         return 1 + fCommands.size();
374     }
375
376     /**
377      * Returns whether the two given commands intersect.
378      *
379      * @param command0 the first command
380      * @param command1 the second command
381      * @return <code>true</code> if the commands intersect
382      * @since 2.1
383      */

384     private boolean intersects(Command command0, Command command1) {
385         // diff middle points if not intersecting
386
if (command0.fOffset + command0.fLength <= command1.fOffset || command1.fOffset + command1.fLength <= command0.fOffset)
387             return (2 * command0.fOffset + command0.fLength) - (2 * command1.fOffset + command1.fLength) == 0;
388         return true;
389     }
390
391     /**
392      * Returns whether the given command intersects with this command.
393      *
394      * @param command the command
395      * @return <code>true</code> if the command intersects with this command
396      * @since 2.1
397      */

398     private boolean intersects(Command command) {
399         // diff middle points if not intersecting
400
if (offset + length <= command.fOffset || command.fOffset + command.fLength <= offset)
401             return (2 * offset + length) - (2 * command.fOffset + command.fLength) == 0;
402         return true;
403     }
404
405     /**
406      * Executes the document commands on a document.
407      *
408      * @param document the document on which to execute the commands
409      * @throws BadLocationException in case access to the given document fails
410      * @since 2.1
411      */

412     void execute(IDocument document) throws BadLocationException {
413
414         if (length == 0 && text == null && fCommands.size() == 0)
415             return;
416
417         DefaultPositionUpdater updater= new DefaultPositionUpdater(getCategory());
418         Position caretPosition= null;
419         try {
420             if (updateCaret()) {
421                 document.addPositionCategory(getCategory());
422                 document.addPositionUpdater(updater);
423                 caretPosition= new Position(caretOffset);
424                 document.addPosition(getCategory(), caretPosition);
425             }
426
427             final Command originalCommand= new Command(offset, length, text, owner);
428             for (final Iterator JavaDoc iterator= new CommandIterator(fCommands, originalCommand, false); iterator.hasNext(); )
429                 ((Command) iterator.next()).execute(document);
430
431         } catch (BadLocationException e) {
432             // ignore
433
} catch (BadPositionCategoryException e) {
434             // ignore
435
} finally {
436             if (updateCaret()) {
437                 document.removePositionUpdater(updater);
438                 try {
439                     document.removePositionCategory(getCategory());
440                 } catch (BadPositionCategoryException e) {
441                     Assert.isTrue(false);
442                 }
443                 caretOffset= caretPosition.getOffset();
444             }
445         }
446     }
447
448     /**
449      * Returns <code>true</code> if the caret offset should be updated, <code>false</code> otherwise.
450      *
451      * @return <code>true</code> if the caret offset should be updated, <code>false</code> otherwise
452      * @since 3.0
453      */

454     private boolean updateCaret() {
455         return shiftsCaret && caretOffset != -1;
456     }
457
458     /**
459      * Returns the position category for the caret offset position.
460      *
461      * @return the position category for the caret offset position
462      * @since 3.0
463      */

464     private String JavaDoc getCategory() {
465         return toString();
466     }
467
468 }
469
Popular Tags