Mercurial > projects > dwt-addons
annotate dwtx/text/edits/MoveSourceEdit.d @ 153:f70d9508c95c
Fix java Collection imports
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 25 Aug 2008 00:27:31 +0200 |
parents | 5cf141e43417 |
children | 7926b636c282 |
rev | line source |
---|---|
129 | 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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.text.edits.MoveSourceEdit; | |
14 | |
131 | 15 import dwtx.text.edits.MultiTextEdit; // packageimport |
16 import dwtx.text.edits.CopySourceEdit; // packageimport | |
17 import dwtx.text.edits.CopyingRangeMarker; // packageimport | |
18 import dwtx.text.edits.ReplaceEdit; // packageimport | |
19 import dwtx.text.edits.EditDocument; // packageimport | |
20 import dwtx.text.edits.UndoCollector; // packageimport | |
21 import dwtx.text.edits.DeleteEdit; // packageimport | |
22 import dwtx.text.edits.MoveTargetEdit; // packageimport | |
23 import dwtx.text.edits.CopyTargetEdit; // packageimport | |
24 import dwtx.text.edits.TextEditCopier; // packageimport | |
25 import dwtx.text.edits.ISourceModifier; // packageimport | |
26 import dwtx.text.edits.TextEditMessages; // packageimport | |
27 import dwtx.text.edits.TextEditProcessor; // packageimport | |
28 import dwtx.text.edits.MalformedTreeException; // packageimport | |
29 import dwtx.text.edits.TreeIterationInfo; // packageimport | |
30 import dwtx.text.edits.TextEditVisitor; // packageimport | |
31 import dwtx.text.edits.TextEditGroup; // packageimport | |
32 import dwtx.text.edits.TextEdit; // packageimport | |
33 import dwtx.text.edits.RangeMarker; // packageimport | |
34 import dwtx.text.edits.UndoEdit; // packageimport | |
35 import dwtx.text.edits.InsertEdit; // packageimport | |
36 | |
37 | |
129 | 38 import dwt.dwthelper.utils; |
39 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
40 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
41 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
42 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
43 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
44 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
45 |
129 | 46 |
47 import dwtx.core.runtime.Assert; | |
48 import dwtx.jface.text.BadLocationException; | |
49 import dwtx.jface.text.IDocument; | |
50 import dwtx.jface.text.IRegion; | |
51 import dwtx.jface.text.Region; | |
52 | |
53 /** | |
54 * A move source edit denotes the source of a move operation. Move | |
55 * source edits are only valid inside an edit tree if they have a | |
56 * corresponding target edit. Furthermore the corresponding target | |
57 * edit can't be a direct or indirect child of the source edit. | |
58 * Violating one of two requirements will result in a <code> | |
59 * MalformedTreeException</code> when executing the edit tree. | |
60 * <p> | |
61 * A move source edit can manage an optional source modifier. A | |
62 * source modifier can provide a set of replace edits which will | |
63 * to applied to the source before it gets inserted at the target | |
64 * position. | |
65 * | |
66 * @see dwtx.text.edits.MoveTargetEdit | |
67 * @see dwtx.text.edits.CopySourceEdit | |
68 * | |
69 * @since 3.0 | |
70 */ | |
71 public final class MoveSourceEdit : TextEdit { | |
72 | |
73 private MoveTargetEdit fTarget; | |
74 private ISourceModifier fModifier; | |
75 | |
76 private String fSourceContent; | |
77 private MultiTextEdit fSourceRoot; | |
78 | |
79 /** | |
80 * Constructs a new move source edit. | |
81 * | |
82 * @param offset the edit's offset | |
83 * @param length the edit's length | |
84 */ | |
130 | 85 public this(int offset, int length) { |
129 | 86 super(offset, length); |
87 } | |
88 | |
89 /** | |
90 * Constructs a new copy source edit. | |
91 * | |
92 * @param offset the edit's offset | |
93 * @param length the edit's length | |
94 * @param target the edit's target | |
95 */ | |
130 | 96 public this(int offset, int length, MoveTargetEdit target) { |
129 | 97 this(offset, length); |
98 setTargetEdit(target); | |
99 } | |
100 | |
101 /* | |
102 * Copy constructor | |
103 */ | |
130 | 104 private this(MoveSourceEdit other) { |
129 | 105 super(other); |
106 if (other.fModifier !is null) | |
107 fModifier= other.fModifier.copy(); | |
108 } | |
109 | |
110 /** | |
111 * Returns the associated target edit or <code>null</code> | |
112 * if no target edit is associated yet. | |
113 * | |
114 * @return the target edit or <code>null</code> | |
115 */ | |
150 | 116 public MoveTargetEdit getTargetEdit() { |
129 | 117 return fTarget; |
118 } | |
119 | |
120 /** | |
121 * Sets the target edit. | |
122 * | |
123 * @param edit the new target edit. | |
124 * | |
125 * @exception MalformedTreeException is thrown if the target edit | |
126 * is a direct or indirect child of the source edit | |
127 */ | |
128 public void setTargetEdit(MoveTargetEdit edit) { | |
129 fTarget= edit; | |
130 fTarget.setSourceEdit(this); | |
131 } | |
132 | |
133 /** | |
134 * Returns the current source modifier or <code>null</code> | |
135 * if no source modifier is set. | |
136 * | |
137 * @return the source modifier | |
138 */ | |
139 public ISourceModifier getSourceModifier() { | |
140 return fModifier; | |
141 } | |
142 | |
143 /** | |
144 * Sets the optional source modifier. | |
145 * | |
146 * @param modifier the source modifier or <code>null</code> | |
147 * if no source modification is need. | |
148 */ | |
149 public void setSourceModifier(ISourceModifier modifier) { | |
150 fModifier= modifier; | |
151 } | |
152 | |
153 //---- API for MoveTargetEdit --------------------------------------------- | |
154 | |
155 String getContent() { | |
156 // The source content can be null if the edit wasn't executed | |
157 // due to an exclusion list of the text edit processor. Return | |
158 // the empty string which can be moved without any harm. | |
159 if (fSourceContent is null) | |
160 return ""; //$NON-NLS-1$ | |
161 return fSourceContent; | |
162 } | |
163 | |
164 MultiTextEdit getSourceRoot() { | |
165 return fSourceRoot; | |
166 } | |
167 | |
168 void clearContent() { | |
169 fSourceContent= null; | |
170 fSourceRoot= null; | |
171 } | |
172 | |
173 //---- Copying ------------------------------------------------------------- | |
174 | |
175 /* | |
176 * @see TextEdit#doCopy | |
177 */ | |
178 protected TextEdit doCopy() { | |
179 return new MoveSourceEdit(this); | |
180 } | |
181 | |
182 /* | |
183 * @see TextEdit#postProcessCopy | |
184 */ | |
185 protected void postProcessCopy(TextEditCopier copier) { | |
186 if (fTarget !is null) { | |
134 | 187 MoveSourceEdit source= cast(MoveSourceEdit)copier.getCopy(this); |
188 MoveTargetEdit target= cast(MoveTargetEdit)copier.getCopy(fTarget); | |
129 | 189 if (source !is null && target !is null) |
190 source.setTargetEdit(target); | |
191 } | |
192 } | |
193 | |
194 //---- Visitor ------------------------------------------------------------- | |
195 | |
196 /* | |
197 * @see TextEdit#accept0 | |
198 */ | |
199 protected void accept0(TextEditVisitor visitor) { | |
200 bool visitChildren= visitor.visit(this); | |
201 if (visitChildren) { | |
202 acceptChildren(visitor); | |
203 } | |
204 } | |
205 | |
206 //---- consistency check ---------------------------------------------------------------- | |
207 | |
208 int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List sourceEdits) { | |
209 int result= super.traverseConsistencyCheck(processor, document, sourceEdits); | |
210 // Since source computation takes place in a recursive fashion (see | |
211 // performSourceComputation) we only do something if we don't have a | |
212 // computed source already. | |
213 if (fSourceContent is null) { | |
214 if (sourceEdits.size() <= result) { | |
215 List list= new ArrayList(); | |
216 list.add(this); | |
217 for (int i= sourceEdits.size(); i < result; i++) | |
218 sourceEdits.add(null); | |
219 sourceEdits.add(list); | |
220 } else { | |
134 | 221 List list= cast(List)sourceEdits.get(result); |
129 | 222 if (list is null) { |
223 list= new ArrayList(); | |
224 sourceEdits.add(result, list); | |
225 } | |
226 list.add(this); | |
227 } | |
228 } | |
229 return result; | |
230 } | |
231 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
232 void performConsistencyCheck(TextEditProcessor processor, IDocument document) { |
129 | 233 if (fTarget is null) |
234 throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.no_target")); //$NON-NLS-1$ | |
235 if (fTarget.getSourceEdit() !is this) | |
236 throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.different_source")); //$NON-NLS-1$ | |
237 /* Causes AST rewrite to fail | |
238 if (getRoot() !is fTarget.getRoot()) | |
239 throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.different_tree")); //$NON-NLS-1$ | |
240 */ | |
241 } | |
242 | |
243 //---- source computation -------------------------------------------------------------- | |
244 | |
245 void traverseSourceComputation(TextEditProcessor processor, IDocument document) { | |
246 // always perform source computation independent of processor.considerEdit | |
247 // The target might need the source and the source is computed in a | |
248 // temporary buffer. | |
249 performSourceComputation(processor, document); | |
250 } | |
251 | |
252 void performSourceComputation(TextEditProcessor processor, IDocument document) { | |
253 try { | |
254 TextEdit[] children= removeChildren(); | |
255 if (children.length > 0) { | |
256 String content= document.get(getOffset(), getLength()); | |
257 EditDocument subDocument= new EditDocument(content); | |
258 fSourceRoot= new MultiTextEdit(getOffset(), getLength()); | |
259 fSourceRoot.addChildren(children); | |
260 fSourceRoot.internalMoveTree(-getOffset()); | |
261 int processingStyle= getStyle(processor); | |
262 TextEditProcessor subProcessor= TextEditProcessor.createSourceComputationProcessor(subDocument, fSourceRoot, processingStyle); | |
263 subProcessor.performEdits(); | |
264 if (needsTransformation()) | |
265 applyTransformation(subDocument, processingStyle); | |
266 fSourceContent= subDocument.get(); | |
267 } else { | |
268 fSourceContent= document.get(getOffset(), getLength()); | |
269 if (needsTransformation()) { | |
270 EditDocument subDocument= new EditDocument(fSourceContent); | |
271 applyTransformation(subDocument, getStyle(processor)); | |
272 fSourceContent= subDocument.get(); | |
273 } | |
274 } | |
275 } catch (BadLocationException cannotHappen) { | |
276 Assert.isTrue(false); | |
277 } | |
278 } | |
279 | |
280 private int getStyle(TextEditProcessor processor) { | |
281 // we never need undo while performing local edits. | |
282 if ((processor.getStyle() & TextEdit.UPDATE_REGIONS) !is 0) | |
283 return TextEdit.UPDATE_REGIONS; | |
284 return TextEdit.NONE; | |
285 } | |
286 | |
287 //---- document updating ---------------------------------------------------------------- | |
288 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
289 int performDocumentUpdating(IDocument document) { |
129 | 290 document.replace(getOffset(), getLength(), ""); //$NON-NLS-1$ |
291 fDelta= -getLength(); | |
292 return fDelta; | |
293 } | |
294 | |
295 //---- region updating -------------------------------------------------------------- | |
296 | |
297 /* | |
298 * @see TextEdit#deleteChildren | |
299 */ | |
300 bool deleteChildren() { | |
301 return false; | |
302 } | |
303 | |
304 //---- content transformation -------------------------------------------------- | |
305 | |
306 private bool needsTransformation() { | |
307 return fModifier !is null; | |
308 } | |
309 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
134
diff
changeset
|
310 private void applyTransformation(IDocument document, int style) { |
129 | 311 if ((style & TextEdit.UPDATE_REGIONS) !is 0 && fSourceRoot !is null) { |
312 Map editMap= new HashMap(); | |
313 TextEdit newEdit= createEdit(editMap); | |
314 List replaces= new ArrayList(Arrays.asList(fModifier.getModifications(document.get()))); | |
315 insertEdits(newEdit, replaces); | |
316 try { | |
317 newEdit.apply(document, style); | |
318 } catch (BadLocationException cannotHappen) { | |
319 Assert.isTrue(false); | |
320 } | |
321 restorePositions(editMap); | |
322 } else { | |
323 MultiTextEdit newEdit= new MultiTextEdit(0, document.getLength()); | |
324 TextEdit[] replaces= fModifier.getModifications(document.get()); | |
325 for (int i= 0; i < replaces.length; i++) { | |
326 newEdit.addChild(replaces[i]); | |
327 } | |
328 try { | |
329 newEdit.apply(document, style); | |
330 } catch (BadLocationException cannotHappen) { | |
331 Assert.isTrue(false); | |
332 } | |
333 } | |
334 } | |
335 | |
336 private TextEdit createEdit(Map editMap) { | |
337 MultiTextEdit result= new MultiTextEdit(0, fSourceRoot.getLength()); | |
338 editMap.put(result, fSourceRoot); | |
339 createEdit(fSourceRoot, result, editMap); | |
340 return result; | |
341 } | |
342 | |
343 private static void createEdit(TextEdit source, TextEdit target, Map editMap) { | |
344 TextEdit[] children= source.getChildren(); | |
345 for (int i= 0; i < children.length; i++) { | |
346 TextEdit child= children[i]; | |
347 // a deleted child remains deleted even if the temporary buffer | |
348 // gets modified. | |
349 if (child.isDeleted()) | |
350 continue; | |
351 RangeMarker marker= new RangeMarker(child.getOffset(), child.getLength()); | |
352 target.addChild(marker); | |
353 editMap.put(marker, child); | |
354 createEdit(child, marker, editMap); | |
355 } | |
356 } | |
357 | |
358 private void insertEdits(TextEdit root, List edits) { | |
359 while(edits.size() > 0) { | |
134 | 360 ReplaceEdit edit= cast(ReplaceEdit)edits.remove(0); |
129 | 361 insert(root, edit, edits); |
362 } | |
363 } | |
364 private static void insert(TextEdit parent, ReplaceEdit edit, List edits) { | |
365 if (!parent.hasChildren()) { | |
366 parent.addChild(edit); | |
367 return; | |
368 } | |
369 TextEdit[] children= parent.getChildren(); | |
370 // First dive down to find the right parent. | |
371 int removed= 0; | |
372 for (int i= 0; i < children.length; i++) { | |
373 TextEdit child= children[i]; | |
374 if (child.covers(edit)) { | |
375 insert(child, edit, edits); | |
376 return; | |
377 } else if (edit.covers(child)) { | |
378 parent.removeChild(i - removed++); | |
379 edit.addChild(child); | |
380 } else { | |
381 IRegion intersect= intersect(edit, child); | |
382 if (intersect !is null) { | |
383 ReplaceEdit[] splits= splitEdit(edit, intersect); | |
384 insert(child, splits[0], edits); | |
385 edits.add(splits[1]); | |
386 return; | |
387 } | |
388 } | |
389 } | |
390 parent.addChild(edit); | |
391 } | |
392 | |
393 public static IRegion intersect(TextEdit op1, TextEdit op2) { | |
394 int offset1= op1.getOffset(); | |
395 int length1= op1.getLength(); | |
396 int end1= offset1 + length1 - 1; | |
397 int offset2= op2.getOffset(); | |
398 if (end1 < offset2) | |
399 return null; | |
400 int length2= op2.getLength(); | |
401 int end2= offset2 + length2 - 1; | |
402 if (end2 < offset1) | |
403 return null; | |
130 | 404 |
129 | 405 int end= Math.min(end1, end2); |
406 if (offset1 < offset2) { | |
407 return new Region(offset2, end - offset2 + 1); | |
408 } | |
409 return new Region(offset1, end - offset1 + 1); | |
410 } | |
411 | |
412 private static ReplaceEdit[] splitEdit(ReplaceEdit edit, IRegion intersect) { | |
413 if (edit.getOffset() !is intersect.getOffset()) | |
414 return splitIntersectRight(edit, intersect); | |
415 return splitIntersectLeft(edit, intersect); | |
416 } | |
417 | |
418 private static ReplaceEdit[] splitIntersectRight(ReplaceEdit edit, IRegion intersect) { | |
419 ReplaceEdit[] result= new ReplaceEdit[2]; | |
420 // this is the actual delete. We use replace to only deal with one type | |
421 result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), ""); //$NON-NLS-1$ | |
422 result[1]= new ReplaceEdit( | |
423 edit.getOffset(), | |
424 intersect.getOffset() - edit.getOffset(), | |
425 edit.getText()); | |
426 return result; | |
427 } | |
428 | |
429 private static ReplaceEdit[] splitIntersectLeft(ReplaceEdit edit, IRegion intersect) { | |
430 ReplaceEdit[] result= new ReplaceEdit[2]; | |
431 result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), edit.getText()); | |
432 result[1]= new ReplaceEdit( // this is the actual delete. We use replace to only deal with one type | |
433 intersect.getOffset() + intersect.getLength(), | |
434 edit.getLength() - intersect.getLength(), | |
435 ""); //$NON-NLS-1$ | |
436 return result; | |
437 } | |
438 | |
439 private static void restorePositions(Map editMap) { | |
440 for (Iterator iter= editMap.keySet().iterator(); iter.hasNext();) { | |
134 | 441 TextEdit marker= cast(TextEdit)iter.next(); |
442 TextEdit edit= cast(TextEdit)editMap.get(marker); | |
129 | 443 if (marker.isDeleted()) { |
444 edit.markAsDeleted(); | |
445 } else { | |
446 edit.adjustOffset(marker.getOffset() - edit.getOffset()); | |
447 edit.adjustLength(marker.getLength() - edit.getLength()); | |
448 } | |
449 } | |
450 } | |
451 } |