KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ant > internal > ui > editor > AntAutoEditStrategy


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 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.ant.internal.ui.editor;
13 import org.eclipse.ant.internal.ui.AntUIPlugin;
14 import org.eclipse.ant.internal.ui.editor.formatter.XmlDocumentFormatter;
15 import org.eclipse.ant.internal.ui.model.AntElementNode;
16 import org.eclipse.ant.internal.ui.model.AntModel;
17 import org.eclipse.jface.text.BadLocationException;
18 import org.eclipse.jface.text.DefaultIndentLineAutoEditStrategy;
19 import org.eclipse.jface.text.Document;
20 import org.eclipse.jface.text.DocumentCommand;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.IRegion;
23 import org.eclipse.jface.text.TextUtilities;
24 import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
25
26 /**
27  * Auto edit strategy for Ant build files
28  * Current does special indenting.
29  */

30 public class AntAutoEditStrategy extends DefaultIndentLineAutoEditStrategy {
31     
32     private AntModel fModel;
33     private int fAccumulatedChange= 0;
34     
35     public AntAutoEditStrategy(AntModel model) {
36         fModel= model;
37     }
38     
39     /**
40      * Sets the indentation based on the Ant element node that contains the offset
41      * of the document command.
42      *
43      * @param d the document to work on
44      * @param c the command to deal with
45      */

46     private synchronized void autoIndentAfterNewLine(IDocument d, DocumentCommand c) {
47         
48         if (c.offset == -1 || d.getLength() == 0 || fModel.getProjectNode(false) == null) {
49             return;
50         }
51         
52         int position= (c.offset == d.getLength() ? c.offset - 1 : c.offset);
53         AntElementNode node= fModel.getProjectNode(false).getNode(position - fAccumulatedChange);
54         if (node == null) {
55             return;
56         }
57         
58         try {
59             StringBuffer JavaDoc correct= XmlDocumentFormatter.getLeadingWhitespace(node.getOffset(), d);
60             if (!nextNodeIsEndTag(c.offset, d)) {
61                 correct.append(XmlDocumentFormatter.createIndent());
62             }
63             StringBuffer JavaDoc buf= new StringBuffer JavaDoc(c.text);
64             buf.append(correct);
65             fAccumulatedChange+= buf.length();
66             
67             int line= d.getLineOfOffset(position);
68             IRegion reg= d.getLineInformation(line);
69             int lineEnd= reg.getOffset() + reg.getLength();
70             int contentStart= findEndOfWhiteSpace(d, c.offset, lineEnd);
71             
72             c.length= Math.max(contentStart - c.offset, 0);
73             c.caretOffset= c.offset + buf.length();
74             c.shiftsCaret= false;
75             c.text= buf.toString();
76     
77         } catch (BadLocationException e) {
78             AntUIPlugin.log(e);
79         }
80     }
81     
82     private boolean nextNodeIsEndTag(int offset, IDocument document) {
83         if (offset + 1 > document.getLength()) {
84             return false;
85         }
86         try {
87             IRegion lineRegion= document.getLineInformationOfOffset(offset);
88             offset= findEndOfWhiteSpace(document, offset, lineRegion.getOffset() + lineRegion.getLength());
89             String JavaDoc nextChars= document.get(offset, 2).trim();
90             if ("</".equals(nextChars) || "/>".equals(nextChars)) { //$NON-NLS-1$ //$NON-NLS-2$
91
return true;
92             }
93         } catch (BadLocationException e) {
94         }
95         return false;
96     }
97
98     /* (non-Javadoc)
99      * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
100      */

101     public void customizeDocumentCommand(IDocument d, DocumentCommand c) {
102         
103         if (c.length == 0 && c.text != null && isLineDelimiter(d, c.text)) {
104             autoIndentAfterNewLine(d, c);
105         } else if (c.text.length() > 1) {
106             smartPaste(d, c);
107         }
108     }
109     
110      private boolean isLineDelimiter(IDocument document, String JavaDoc text) {
111         String JavaDoc[] delimiters= document.getLegalLineDelimiters();
112         if (delimiters != null)
113             return TextUtilities.equals(delimiters, text) > -1;
114         return false;
115     }
116     
117     public synchronized void reconciled() {
118         fAccumulatedChange= 0;
119     }
120     
121     private void smartPaste(IDocument document, DocumentCommand command) {
122         try {
123             if (command.offset == -1 || document.getLength() == 0 || fModel.getProjectNode(false) == null) {
124                 return;
125             }
126             String JavaDoc origChange= command.text;
127             int position= (command.offset == document.getLength() ? command.offset - 1 : command.offset);
128             AntElementNode node= fModel.getProjectNode(false).getNode(position - fAccumulatedChange);
129             if (node == null) {
130                 return;
131             }
132             
133             // eat any WS before the insertion to the beginning of the line
134
int firstLine= 1; // don't format the first line if it has other content before it
135
IRegion line= document.getLineInformationOfOffset(command.offset);
136             String JavaDoc notSelected= document.get(line.getOffset(), command.offset - line.getOffset());
137             if (notSelected.trim().length() == 0) {
138                 command.length += notSelected.length();
139                 command.offset= line.getOffset();
140                 firstLine= 0;
141             }
142             
143             // handle the indentation computation inside a temporary document
144
Document temp= new Document(command.text);
145             
146             // indent the first and second line
147
// compute the relative indentation difference from the second line
148
// (as the first might be partially selected) and use the value to
149
// indent all other lines.
150
boolean isIndentDetected= false;
151             StringBuffer JavaDoc addition= new StringBuffer JavaDoc();
152             int insertLength= 0;
153             int lines= temp.getNumberOfLines();
154             for (int l= firstLine; l < lines; l++) { // we don't change the number of lines while adding indents
155

156                 IRegion r= temp.getLineInformation(l);
157                 int lineOffset= r.getOffset();
158                 int lineLength= r.getLength();
159                 
160                 if (lineLength == 0) { // don't modify empty lines
161
continue;
162                 }
163                 
164                 if (!isIndentDetected){
165                     
166                     // indent the first pasted line
167
StringBuffer JavaDoc current= XmlDocumentFormatter.getLeadingWhitespace(lineOffset, temp);
168                     StringBuffer JavaDoc correct= XmlDocumentFormatter.getLeadingWhitespace(node.getOffset(), document);
169                     correct.append(XmlDocumentFormatter.createIndent());
170                     
171                     insertLength= subtractIndent(correct, current, addition);
172                     isIndentDetected= true;
173                 }
174                 
175                 // relatively indent all pasted lines
176
if (insertLength > 0) {
177                     addIndent(temp, l, addition);
178                 } else if (insertLength < 0) {
179                     cutIndent(temp, l, -insertLength);
180                 }
181             }
182             
183             // modify the command
184
if (!origChange.equals(temp.get())) {
185                 fAccumulatedChange+= temp.getLength();
186                 command.text= temp.get();
187             }
188             
189         } catch (BadLocationException e) {
190             AntUIPlugin.log(e);
191         }
192     }
193     
194     /**
195      * Indents line <code>line</code> in <code>document</code> with <code>indent</code>.
196      * Leaves leading comment signs alone.
197      *
198      * @param document the document
199      * @param line the line
200      * @param indent the indentation to insert
201      * @throws BadLocationException on concurrent document modification
202      */

203     private void addIndent(Document document, int line, CharSequence JavaDoc indent) throws BadLocationException {
204         IRegion region= document.getLineInformation(line);
205         int insert= region.getOffset();
206         
207         // insert indent
208
document.replace(insert, 0, indent.toString());
209     }
210     
211     /**
212      * Cuts the visual equivalent of <code>toDelete</code> characters out of the
213      * indentation of line <code>line</code> in <code>document</code>.
214      *
215      * @param document the document
216      * @param line the line
217      * @param toDelete the number of space equivalents to delete.
218      * @throws BadLocationException on concurrent document modification
219      */

220     private void cutIndent(Document document, int line, int toDelete) throws BadLocationException {
221         IRegion region= document.getLineInformation(line);
222         int from= region.getOffset();
223         int endOffset= region.getOffset() + region.getLength();
224         
225         int to= from;
226         while (toDelete > 0 && to < endOffset) {
227             char ch= document.getChar(to);
228             if (!Character.isWhitespace(ch))
229                 break;
230             toDelete -= computeVisualLength(ch);
231             if (toDelete >= 0) {
232                 to++;
233             } else {
234                 break;
235             }
236         }
237         
238         document.replace(from, to - from, null);
239     }
240     
241     /**
242      * Returns the visual length of a given character taking into
243      * account the visual tabulator length.
244      *
245      * @param ch the character to measure
246      * @return the visual length of <code>ch</code>
247      */

248     private int computeVisualLength(char ch) {
249         if (ch == '\t') {
250             return getVisualTabLengthPreference();
251         }
252             
253         return 1;
254     }
255     
256     /**
257      * Returns the visual length of a given <code>CharSequence</code> taking into
258      * account the visual tabulator length.
259      *
260      * @param seq the string to measure
261      * @return the visual length of <code>seq</code>
262      */

263     private int computeVisualLength(CharSequence JavaDoc seq) {
264         int size= 0;
265         int tablen= getVisualTabLengthPreference();
266         
267         for (int i= 0; i < seq.length(); i++) {
268             char ch= seq.charAt(i);
269             if (ch == '\t') {
270                 size += tablen - size % tablen;
271             } else {
272                 size++;
273             }
274         }
275         return size;
276     }
277     
278     /**
279      * Computes the difference of two indentations and returns the difference in
280      * length of current and correct. If the return value is positive, <code>addition</code>
281      * is initialized with a substring of that length of <code>correct</code>.
282      *
283      * @param correct the correct indentation
284      * @param current the current indentation (might contain non-whitespace)
285      * @param difference a string buffer - if the return value is positive, it will be cleared and set to the substring of <code>current</code> of that length
286      * @return the difference in length of <code>correct</code> and <code>current</code>
287      */

288     private int subtractIndent(CharSequence JavaDoc correct, CharSequence JavaDoc current, StringBuffer JavaDoc difference) {
289         int c1= computeVisualLength(correct);
290         int c2= computeVisualLength(current);
291         int diff= c1 - c2;
292         if (diff <= 0) {
293             return diff;
294         }
295         
296         difference.setLength(0);
297         int len= 0, i= 0;
298         while (len < diff) {
299             char c= correct.charAt(i++);
300             difference.append(c);
301             len += computeVisualLength(c);
302         }
303         
304         return diff;
305     }
306     
307     /**
308      * The preference setting for the visual tabulator display.
309      *
310      * @return the number of spaces displayed for a tabulator in the editor
311      */

312     private int getVisualTabLengthPreference() {
313         return AntUIPlugin.getDefault().getCombinedPreferenceStore().getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH);
314     }
315 }
316
Popular Tags