KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ltk > core > refactoring > TextEditBasedChange


1 /*******************************************************************************
2  * Copyright (c) 2005, 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  * Ed Swartz <ed.swartz@nokia.com> -
11  * (bug 157203: [ltk] [patch] TextEditBasedChange/TextChange provides incorrect diff when one side is empty)
12  *******************************************************************************/

13 package org.eclipse.ltk.core.refactoring;
14
15 import java.util.ArrayList JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.List JavaDoc;
18
19 import org.eclipse.text.edits.TextEdit;
20 import org.eclipse.text.edits.TextEditCopier;
21 import org.eclipse.text.edits.TextEditGroup;
22 import org.eclipse.text.edits.TextEditProcessor;
23
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.IProgressMonitor;
27
28 import org.eclipse.jface.text.BadLocationException;
29 import org.eclipse.jface.text.IDocument;
30 import org.eclipse.jface.text.IRegion;
31
32 import org.eclipse.ltk.internal.core.refactoring.Changes;
33
34 /**
35  * An abstract base implementation of a change which is based on text edits.
36  *
37  * @since 3.2
38  */

39 public abstract class TextEditBasedChange extends Change {
40
41     /**
42      * Text edit processor which has the ability to selectively include or exclude single text edits.
43      */

44     static final class LocalTextEditProcessor extends TextEditProcessor {
45         public static final int EXCLUDE= 1;
46         public static final int INCLUDE= 2;
47
48         private TextEdit[] fExcludes;
49         private TextEdit[] fIncludes;
50         
51         protected LocalTextEditProcessor(IDocument document, TextEdit root, int flags) {
52             super(document, root, flags);
53         }
54         public void setIncludes(TextEdit[] includes) {
55             Assert.isNotNull(includes);
56             Assert.isTrue(fExcludes == null);
57             fIncludes= flatten(includes);
58         }
59         public void setExcludes(TextEdit[] excludes) {
60             Assert.isNotNull(excludes);
61             Assert.isTrue(fIncludes == null);
62             fExcludes= flatten(excludes);
63         }
64         protected boolean considerEdit(TextEdit edit) {
65             if (fExcludes != null) {
66                 for (int i= 0; i < fExcludes.length; i++) {
67                     if (edit.equals(fExcludes[i]))
68                         return false;
69                 }
70                 return true;
71             }
72             if (fIncludes != null) {
73                 for (int i= 0; i < fIncludes.length; i++) {
74                     if (edit.equals(fIncludes[i]))
75                         return true;
76                 }
77                 return false;
78             }
79             return true;
80         }
81         private TextEdit[] flatten(TextEdit[] edits) {
82             List JavaDoc result= new ArrayList JavaDoc(5);
83             for (int i= 0; i < edits.length; i++) {
84                 flatten(result, edits[i]);
85             }
86             return (TextEdit[])result.toArray(new TextEdit[result.size()]);
87         }
88         private void flatten(List JavaDoc result, TextEdit edit) {
89             result.add(edit);
90             TextEdit[] children= edit.getChildren();
91             for (int i= 0; i < children.length; i++) {
92                 flatten(result, children[i]);
93             }
94         }
95     }
96
97     /**
98      * Value objects encapsulating a document with an associated region.
99      */

100     static final class PreviewAndRegion {
101         public IDocument document;
102         public IRegion region;
103         public PreviewAndRegion(IDocument d, IRegion r) {
104             document= d;
105             region= r;
106         }
107     }
108
109     /**
110      * A special object denoting all edits managed by the change. This even
111      * includes those edits not managed by a {@link TextEditBasedChangeGroup}.
112      */

113     static final TextEditBasedChangeGroup[] ALL_EDITS= new TextEditBasedChangeGroup[0];
114
115     /** The list of change groups */
116     private List JavaDoc fChangeGroups;
117     private GroupCategorySet fCombiedGroupCategories;
118     
119     /** The name of the change */
120     private String JavaDoc fName;
121     
122     /** The text type */
123     private String JavaDoc fTextType;
124     
125     /** Should the positions of edits be tracked during change generation? */
126     private boolean fTrackEdits;
127
128     /**
129      * Creates a new abstract text edit change with the specified name. The name is a
130      * human-readable value that is displayed to users. The name does not
131      * need to be unique, but it must not be <code>null</code>.
132      * <p>
133      * The text type of this text edit change is set to <code>txt</code>.
134      * </p>
135      *
136      * @param name the name of the text edit change
137      *
138      * @see #setTextType(String)
139      */

140     protected TextEditBasedChange(String JavaDoc name) {
141         Assert.isNotNull(name, "Name must not be null"); //$NON-NLS-1$
142
fChangeGroups= new ArrayList JavaDoc(5);
143         fName= name;
144         fTextType= "txt"; //$NON-NLS-1$
145
}
146
147     /**
148      * Adds a {@link TextEditBasedChangeGroup text edit change group}.
149      * The edits managed by the given text edit change group must be part of
150      * the change's root edit.
151      *
152      * @param group the text edit change group to add
153      */

154     public void addChangeGroup(TextEditBasedChangeGroup group) {
155         Assert.isTrue(group != null);
156         fChangeGroups.add(group);
157         if (fCombiedGroupCategories != null) {
158             fCombiedGroupCategories= GroupCategorySet.union(fCombiedGroupCategories, group.getGroupCategorySet());
159         }
160     }
161
162     /**
163      * Adds a {@link TextEditGroup text edit group}. This method is a convenience
164      * method for calling <code>change.addChangeGroup(new
165      * TextEditBasedChangeGroup(change, group));</code>.
166      *
167      * @param group the text edit group to add
168      */

169     public void addTextEditGroup(TextEditGroup group) {
170         addChangeGroup(new TextEditBasedChangeGroup(this, group));
171     }
172     
173     /**
174      * Returns <code>true</code> if the change has one of the given group
175      * categories. Otherwise <code>false</code> is returned.
176      *
177      * @param groupCategories the group categories to check
178      *
179      * @return whether the change has one of the given group
180      * categories
181      *
182      * @since 3.2
183      */

184     public boolean hasOneGroupCategory(List JavaDoc groupCategories) {
185         if (fCombiedGroupCategories == null) {
186             fCombiedGroupCategories= GroupCategorySet.NONE;
187             for (Iterator JavaDoc iter= fChangeGroups.iterator(); iter.hasNext();) {
188                 TextEditBasedChangeGroup group= (TextEditBasedChangeGroup)iter.next();
189                 fCombiedGroupCategories= GroupCategorySet.union(fCombiedGroupCategories, group.getGroupCategorySet());
190             }
191         }
192         return fCombiedGroupCategories.containsOneCategory(groupCategories);
193     }
194
195     /**
196      * Returns the {@link TextEditBasedChangeGroup text edit change groups} managed by this
197      * buffer change.
198      *
199      * @return the text edit change groups
200      */

201     public final TextEditBasedChangeGroup[] getChangeGroups() {
202         return (TextEditBasedChangeGroup[])fChangeGroups.toArray(new TextEditBasedChangeGroup[fChangeGroups.size()]);
203     }
204
205     String JavaDoc getContent(IDocument document, IRegion region, boolean expandRegionToFullLine, int surroundingLines) throws CoreException {
206         try {
207             if (expandRegionToFullLine) {
208                 int startLine= Math.max(document.getLineOfOffset(region.getOffset()) - surroundingLines, 0);
209                 int endLine;
210                 if (region.getLength() == 0) {
211                     // no lines are in the region, so remove one from the context,
212
// or else spurious changes show up that look like deletes from the source
213
if (surroundingLines == 0) {
214                         // empty: show nothing
215
return ""; //$NON-NLS-1$
216
}
217                     
218                     endLine= Math.min(
219                         document.getLineOfOffset(region.getOffset()) + surroundingLines - 1,
220                         document.getNumberOfLines() - 1);
221                 } else {
222                     endLine= Math.min(
223                         document.getLineOfOffset(region.getOffset() + region.getLength() - 1) + surroundingLines,
224                         document.getNumberOfLines() - 1);
225                 }
226                 
227                 int offset= document.getLineInformation(startLine).getOffset();
228                 IRegion endLineRegion= document.getLineInformation(endLine);
229                 int length = endLineRegion.getOffset() + endLineRegion.getLength() - offset;
230                 return document.get(offset, length);
231                 
232             } else {
233                 return document.get(region.getOffset(), region.getLength());
234             }
235         } catch (BadLocationException e) {
236             throw Changes.asCoreException(e);
237         }
238     }
239     
240     /**
241      * Returns the current content of the document this text
242      * change is associated with.
243      *
244      * @param pm a progress monitor to report progress or <code>null</code>
245      * if no progress reporting is desired
246      * @return the current content of the text edit change
247      *
248      * @exception CoreException if the content can't be accessed
249      */

250     public abstract String JavaDoc getCurrentContent(IProgressMonitor pm) throws CoreException;
251
252     /**
253      * Returns the current content of the text edit change clipped to a specific
254      * region. The region is determined as follows:
255      * <ul>
256      * <li>if <code>expandRegionToFullLine</code> is <code>false</code>
257      * then the parameter <code>region</code> determines the clipping.
258      * </li>
259      * <li>if <code>expandRegionToFullLine</code> is <code>true</code>
260      * then the region determined by the parameter <code>region</code>
261      * is extended to cover full lines.
262      * </li>
263      * <li>if <code>surroundingLines</code> &gt; 0 then the given number
264      * of surrounding lines is added. The value of <code>surroundingLines
265      * </code> is only considered if <code>expandRegionToFullLine</code>
266      * is <code>true</code>
267      * </li>
268      * </ul>
269      *
270      * @param region the starting region for the text to be returned
271      * @param expandRegionToFullLine if <code>true</code> is passed the region
272      * is extended to cover full lines
273      * @param surroundingLines the number of surrounding lines to be added to
274      * the clipping region. Is only considered if <code>expandRegionToFullLine
275      * </code> is <code>true</code>
276      * @param pm a progress monitor to report progress or <code>null</code>
277      * if no progress reporting is desired
278      *
279      * @return the current content of the text edit change clipped to a region
280      * determined by the given parameters.
281      *
282      * @throws CoreException if an exception occurs while accessing the current content
283      */

284     public abstract String JavaDoc getCurrentContent(IRegion region, boolean expandRegionToFullLine, int surroundingLines, IProgressMonitor pm) throws CoreException;
285     
286     /**
287      * Returns whether preview edits are remembered for further region
288      * tracking or not.
289      *
290      * @return <code>true</code> if executed text edits are remembered
291      * during preview generation; otherwise <code>false</code>
292      */

293     public boolean getKeepPreviewEdits() {
294         return fTrackEdits;
295     }
296
297     /**
298      * {@inheritDoc}
299      */

300     public String JavaDoc getName() {
301         return fName;
302     }
303
304     /**
305      * Returns a preview of the text edit change clipped to a specific region.
306      * The preview is created by applying the text edits managed by the
307      * given array of {@link TextEditBasedChangeGroup text edit change groups}.
308      * The region is determined as follows:
309      * <ul>
310      * <li>if <code>expandRegionToFullLine</code> is <code>false</code>
311      * then the parameter <code>region</code> determines the clipping.
312      * </li>
313      * <li>if <code>expandRegionToFullLine</code> is <code>true</code>
314      * then the region determined by the parameter <code>region</code>
315      * is extended to cover full lines.
316      * </li>
317      * <li>if <code>surroundingLines</code> &gt; 0 then the given number
318      * of surrounding lines is added. The value of <code>surroundingLines
319      * </code> is only considered if <code>expandRegionToFullLine</code>
320      * is <code>true</code>
321      * </li>
322      * </ul>
323      *
324      * @param changeGroups a set of change groups for which a preview is to be
325      * generated
326      * @param region the starting region for the clipping
327      * @param expandRegionToFullLine if <code>true</code> is passed the region
328      * is extended to cover full lines
329      * @param surroundingLines the number of surrounding lines to be added to
330      * the clipping region. Is only considered if <code>expandRegionToFullLine
331      * </code> is <code>true</code>
332      * @param pm a progress monitor to report progress or <code>null</code>
333      * if no progress reporting is desired
334      *
335      * @return the current content of the text change clipped to a region
336      * determined by the given parameters.
337      *
338      * @throws CoreException if an exception occurs while generating the preview
339      *
340      * @see #getCurrentContent(IRegion, boolean, int, IProgressMonitor)
341      */

342     public abstract String JavaDoc getPreviewContent(TextEditBasedChangeGroup[] changeGroups, IRegion region, boolean expandRegionToFullLine, int surroundingLines, IProgressMonitor pm) throws CoreException;
343
344     /**
345      * Returns the preview content as a string.
346      *
347      * @param pm a progress monitor to report progress or <code>null</code>
348      * if no progress reporting is desired
349      * @return the preview
350      *
351      * @throws CoreException if the preview can't be created
352      */

353     public abstract String JavaDoc getPreviewContent(IProgressMonitor pm) throws CoreException;
354
355     /**
356      * Returns the text edit change's text type.
357      *
358      * @return the text edit change's text type
359      */

360     public String JavaDoc getTextType() {
361         return fTextType;
362     }
363
364     TextEdit[] mapEdits(TextEdit[] edits, TextEditCopier copier) {
365         if (edits == null)
366             return null;
367         final List JavaDoc result= new ArrayList JavaDoc(edits.length);
368         for (int i= 0; i < edits.length; i++) {
369             TextEdit edit= copier.getCopy(edits[i]);
370             if (edit != null)
371                 result.add(edit);
372         }
373         return (TextEdit[]) result.toArray(new TextEdit[result.size()]);
374     }
375
376     /**
377      * {@inheritDoc}
378      */

379     public void setEnabled(boolean enabled) {
380         super.setEnabled(enabled);
381         for (Iterator JavaDoc iter= fChangeGroups.iterator(); iter.hasNext();) {
382             TextEditBasedChangeGroup element= (TextEditBasedChangeGroup) iter.next();
383             element.setEnabled(enabled);
384         }
385     }
386
387     /**
388      * Controls whether the text edit change should keep executed edits during
389      * preview generation.
390      *
391      * @param keep if <code>true</code> executed preview edits are kept
392      */

393     public void setKeepPreviewEdits(boolean keep) {
394         fTrackEdits= keep;
395     }
396
397     /**
398      * Sets the text type. The text type is used to determine the content
399      * merge viewer used to present the difference between the original
400      * and the preview content in the user interface. Content merge viewers
401      * are defined via the extension point <code>org.eclipse.compare.contentMergeViewers</code>.
402      * <p>
403      * The default text type is <code>txt</code>.
404      * </p>
405      *
406      * @param type the text type. If <code>null</code> is passed the text type is
407      * reseted to the default text type <code>txt</code>.
408      */

409     public void setTextType(String JavaDoc type) {
410         if (type == null)
411             type= "txt"; //$NON-NLS-1$
412
fTextType= type;
413     }
414 }
415
Popular Tags