View | Details | Raw Unified | Return to bug 40255 | Differences between
and this patch

Collapse All | Expand All

(-).project (-9 lines)
Lines 5-26 Link Here
5
	<projects>
5
	<projects>
6
		<project>org.apache.ant</project>
6
		<project>org.apache.ant</project>
7
		<project>org.eclipse.ant.core</project>
7
		<project>org.eclipse.ant.core</project>
8
		<project>org.eclipse.core.resources</project>
9
		<project>org.eclipse.core.runtime.compatibility</project>
10
		<project>org.eclipse.core.variables</project>
8
		<project>org.eclipse.core.variables</project>
11
		<project>org.eclipse.debug.core</project>
9
		<project>org.eclipse.debug.core</project>
12
		<project>org.eclipse.debug.ui</project>
10
		<project>org.eclipse.debug.ui</project>
13
		<project>org.eclipse.jdt.debug.ui</project>
11
		<project>org.eclipse.jdt.debug.ui</project>
14
		<project>org.eclipse.jdt.launching</project>
12
		<project>org.eclipse.jdt.launching</project>
15
		<project>org.eclipse.jdt.ui</project>
16
		<project>org.eclipse.jface.text</project>
17
		<project>org.eclipse.ui</project>
18
		<project>org.eclipse.ui.console</project>
13
		<project>org.eclipse.ui.console</project>
19
		<project>org.eclipse.ui.editors</project>
20
		<project>org.eclipse.ui.externaltools</project>
14
		<project>org.eclipse.ui.externaltools</project>
21
		<project>org.eclipse.ui.ide</project>
22
		<project>org.eclipse.ui.views</project>
23
		<project>org.eclipse.ui.workbench.texteditor</project>
24
	</projects>
15
	</projects>
25
	<buildSpec>
16
	<buildSpec>
26
		<buildCommand>
17
		<buildCommand>
(-)Ant Editor/org/eclipse/ant/internal/ui/editor/formatter/FormattingPreferences.java (-5 / +1 lines)
Lines 38-44 Link Here
38
        return fPrefs.getInt(AntEditorPreferenceConstants.FORMATTER_MAX_LINE_LENGTH);
38
        return fPrefs.getInt(AntEditorPreferenceConstants.FORMATTER_MAX_LINE_LENGTH);
39
    }  
39
    }  
40
    
40
    
41
    public boolean useElementWrapping() {
41
    public boolean wrapLongTags() {
42
        return fPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_WRAP_LONG);
42
        return fPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_WRAP_LONG);
43
    }
43
    }
44
    
44
    
Lines 52-61 Link Here
52
    
52
    
53
    public boolean stripBlankLines() {
53
    public boolean stripBlankLines() {
54
        return fPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_DELETE_BLANK_LINES);
54
        return fPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_DELETE_BLANK_LINES);
55
    }
56
57
    public boolean formatElements() {
58
        return fPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_WRAP_LONG);
59
    }
55
    }
60
56
61
	public int getTabWidth() {
57
	public int getTabWidth() {
(-)Ant Editor/org/eclipse/ant/internal/ui/editor/formatter/NonParsingXMLFormatter.java (-410 lines)
Removed Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 John-Mason P. Shackelford 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
 *     John-Mason P. Shackelford - initial API and implementation
10
 * 	   IBM Corporation - bug 52076
11
 *******************************************************************************/
12
13
package org.eclipse.ant.internal.ui.editor.formatter;
14
15
import java.io.IOException;
16
import java.io.Reader;
17
import java.io.StringReader;
18
19
import org.eclipse.ant.internal.ui.model.AntUIPlugin;
20
import org.eclipse.jface.text.Assert;
21
22
public class NonParsingXMLFormatter {
23
24
    private static class CommentReader extends TagReader {
25
26
        private boolean complete = false;
27
28
        protected void clear() {
29
            this.complete = false;
30
        }
31
32
        public String getStartOfTag() {
33
            return "<!--"; //$NON-NLS-1$
34
        }
35
36
        protected String readTag() throws IOException {
37
            int intChar;
38
            char c;
39
            StringBuffer node = new StringBuffer();
40
41
            while (!complete && (intChar = reader.read()) != -1) {
42
                c = (char) intChar;
43
44
                node.append(c);
45
46
                if (c == '>' && node.toString().endsWith("-->")) { //$NON-NLS-1$
47
                    complete = true;
48
                }
49
            }
50
            return node.toString();
51
        }
52
    }
53
54
    private static class DoctypeDeclarationReader extends TagReader {
55
56
        private boolean complete = false;
57
58
        protected void clear() {
59
            this.complete = false;
60
        }
61
62
        public String getStartOfTag() {
63
            return "<!"; //$NON-NLS-1$
64
        }
65
66
        protected String readTag() throws IOException {
67
            int intChar;
68
            char c;
69
            StringBuffer node = new StringBuffer();
70
71
            while (!complete && (intChar = reader.read()) != -1) {
72
                c = (char) intChar;
73
74
                node.append(c);
75
76
                if (c == '>') {
77
                    complete = true;
78
                }
79
            }
80
            return node.toString();
81
        }
82
83
    }
84
85
    private static class ProcessingInstructionReader extends TagReader {
86
87
        private boolean complete = false;
88
89
        protected void clear() {
90
            this.complete = false;
91
        }
92
93
        public String getStartOfTag() {
94
            return "<?"; //$NON-NLS-1$
95
        }
96
97
        protected String readTag() throws IOException {
98
            int intChar;
99
            char c;
100
            StringBuffer node = new StringBuffer();
101
102
            while (!complete && (intChar = reader.read()) != -1) {
103
                c = (char) intChar;
104
105
                node.append(c);
106
107
                if (c == '>' && node.toString().endsWith("?>")) { //$NON-NLS-1$
108
                    complete = true;
109
                }
110
            }
111
            return node.toString();
112
        }
113
    }
114
115
    private static abstract class TagReader {
116
117
        protected Reader reader;
118
119
        private String tagText;
120
121
        protected abstract void clear();
122
123
        public int getPostTagDepthModifier() {
124
            return 0;
125
        }
126
127
        public int getPreTagDepthModifier() {
128
            return 0;
129
        }
130
131
        abstract public String getStartOfTag();
132
133
        public String getTagText() {
134
            return this.tagText;
135
        }
136
137
        public boolean isTextNode() {
138
            return false;
139
        }
140
141
        protected abstract String readTag() throws IOException;
142
143
        public boolean requiresInitialIndent() {
144
            return true;
145
        }
146
147
        public void setReader(Reader reader) throws IOException {
148
            this.reader = reader;
149
            this.clear();
150
            this.tagText = readTag();
151
        }
152
153
        public boolean startsOnNewline() {
154
            return true;
155
        }
156
    }
157
158
    private static class TagReaderFactory {
159
160
        // Warning: the order of the Array is important!
161
        private static TagReader[] tagReaders = new TagReader[]{new CommentReader(),
162
                                                                new DoctypeDeclarationReader(),
163
                                                                new ProcessingInstructionReader(),
164
                                                                new XmlElementReader()};
165
166
        private static TagReader textNodeReader = new TextReader();
167
168
        public static TagReader createTagReaderFor(Reader reader)
169
                                                                 throws IOException {
170
171
            char[] buf = new char[10];
172
            reader.mark(10);
173
            reader.read(buf, 0, 10);
174
            reader.reset();
175
176
            String startOfTag = String.valueOf(buf);
177
178
            for (int i = 0; i < tagReaders.length; i++) {
179
                if (startOfTag.startsWith(tagReaders[i].getStartOfTag())) {
180
                    tagReaders[i].setReader(reader);
181
                    return tagReaders[i];
182
                }
183
            }
184
            // else
185
            textNodeReader.setReader(reader);
186
            return textNodeReader;
187
        }
188
    }
189
190
    private static class TextReader extends TagReader {
191
192
        private boolean complete;
193
194
        private boolean isTextNode;
195
196
        protected void clear() {
197
            this.complete = false;
198
        }
199
200
        /* (non-Javadoc)
201
         * @see org.eclipse.ant.internal.ui.editor.formatter.NonParsingXMLFormatter.TagReader#getStartOfTag()
202
         */
203
        public String getStartOfTag() {
204
            return ""; //$NON-NLS-1$
205
        }
206
207
        /* (non-Javadoc)
208
         * @see org.eclipse.ant.internal.ui.editor.formatter.NonParsingXMLFormatter.TagReader#isTextNode()
209
         */
210
        public boolean isTextNode() {
211
            return this.isTextNode;
212
        }
213
214
        protected String readTag() throws IOException {
215
216
            StringBuffer node = new StringBuffer();
217
218
            while (!complete) {
219
220
                reader.mark(1);
221
                int intChar = reader.read();
222
                if (intChar == -1) break;
223
224
                char c = (char) intChar;
225
                if (c == '<') {
226
                    reader.reset();
227
                    complete = true;
228
                } else {
229
                    node.append(c);
230
                }
231
            }
232
233
            // if this text node is just whitespace
234
            // remove it, except for the newlines.
235
            if (node.length() < 1) {
236
                this.isTextNode = false;
237
238
            } else if (node.toString().trim().length() == 0) {
239
                String whitespace = node.toString();
240
                node = new StringBuffer();
241
                for (int i = 0; i < whitespace.length(); i++) {
242
                    char whitespaceCharacter = whitespace.charAt(i);
243
                    if (whitespaceCharacter == '\n')
244
                            node.append(whitespaceCharacter);
245
                }
246
                this.isTextNode = false;
247
248
            } else {
249
                this.isTextNode = true;
250
            }
251
            return node.toString();
252
        }
253
254
        /* (non-Javadoc)
255
         * @see org.eclipse.ant.internal.ui.editor.formatter.NonParsingXMLFormatter.TagReader#requiresInitialIndent()
256
         */
257
        public boolean requiresInitialIndent() {
258
            return false;
259
        }
260
261
        /* (non-Javadoc)
262
         * @see org.eclipse.ant.internal.ui.editor.formatter.NonParsingXMLFormatter.TagReader#startsOnNewline()
263
         */
264
        public boolean startsOnNewline() {
265
            return false;
266
        }
267
    }
268
269
    private static class XmlElementReader extends TagReader {
270
271
        private boolean complete = false;
272
273
        protected void clear() {
274
            this.complete = false;
275
        }
276
277
        public int getPostTagDepthModifier() {
278
            if (getTagText().endsWith("/>") || getTagText().endsWith("/ >")) { //$NON-NLS-1$ //$NON-NLS-2$
279
                return 0;
280
            } else if (getTagText().startsWith("</")) { //$NON-NLS-1$
281
                return 0;
282
            } else {
283
                return +1;
284
            }
285
        }
286
287
        public int getPreTagDepthModifier() {
288
            if (getTagText().startsWith("</")) { //$NON-NLS-1$
289
                return -1;
290
            } else {
291
                return 0;
292
            }
293
        }
294
295
        public String getStartOfTag() {
296
            return "<"; //$NON-NLS-1$
297
        }
298
299
        protected String readTag() throws IOException {
300
301
            StringBuffer node = new StringBuffer();
302
303
            boolean insideQuote = false;
304
            int intChar;
305
306
            while (!complete && (intChar = reader.read()) != -1) {
307
                char c = (char) intChar;
308
309
                node.append(c);
310
                // TODO logic incorrectly assumes that " is quote character
311
                // when it could also be '
312
                if (c == '"') {
313
                    insideQuote = !insideQuote;
314
                }
315
                if (c == '>' && !insideQuote) {
316
                    complete = true;
317
                }
318
            }
319
            return node.toString();
320
        }
321
    }
322
323
    private int depth;
324
325
    private String documentText;
326
327
    private StringBuffer formattedXml;
328
329
    private boolean lastNodeWasText;
330
331
    private FormattingPreferences prefs;
332
333
    private void copyNode(Reader reader, StringBuffer out) throws IOException {
334
335
        TagReader tag = TagReaderFactory.createTagReaderFor(reader);
336
337
        depth = depth + tag.getPreTagDepthModifier();
338
339
        if (!lastNodeWasText) {
340
341
            if (tag.startsOnNewline() && !hasNewlineAlready(out)) {
342
                out.append("\n"); //$NON-NLS-1$
343
            }
344
345
            if (tag.requiresInitialIndent()) {
346
                out.append(indent());
347
            }
348
        }
349
350
        out.append(tag.getTagText());
351
352
        depth = depth + tag.getPostTagDepthModifier();
353
354
        lastNodeWasText = tag.isTextNode();
355
356
    }
357
358
    /**
359
     * @return
360
     */
361
    public String format() {
362
363
        Assert.isNotNull(this.documentText);
364
        Assert.isNotNull(this.prefs);
365
366
        Reader reader = new StringReader(documentText);
367
        formattedXml = new StringBuffer();
368
369
        depth = 0;
370
        lastNodeWasText = false;
371
        try {
372
            while (true) {
373
                reader.mark(1);
374
                int intChar = reader.read();
375
                reader.reset();
376
377
                if (intChar != -1) {
378
                    copyNode(reader, formattedXml);
379
                } else {
380
                    break;
381
                }
382
            }
383
            reader.close();
384
        } catch (IOException e) {
385
           AntUIPlugin.log(e);
386
        }
387
        return formattedXml.toString();
388
    }
389
390
    private boolean hasNewlineAlready(StringBuffer out) {
391
        return out.lastIndexOf("\n") == formattedXml.length() - 1 //$NON-NLS-1$
392
               || out.lastIndexOf("\r") == formattedXml.length() - 1; //$NON-NLS-1$
393
    }
394
395
    private String indent() {
396
        StringBuffer indent = new StringBuffer(30);
397
        for (int i = 0; i < depth; i++) {
398
            indent.append(prefs.getCanonicalIndent());
399
        }
400
        return indent.toString();
401
    }
402
403
    public void setFormattingPreferences(FormattingPreferences prefs) {
404
        this.prefs = prefs;
405
    }
406
407
    public void setText(String documentText) {
408
        this.documentText = documentText;
409
    }
410
}
(-)Ant Editor/org/eclipse/ant/internal/ui/editor/formatter/XmlDocumentFormattingStrategy.java (-1 / +1 lines)
Lines 69-75 Link Here
69
        // setup formatter with preferences and format the text.
69
        // setup formatter with preferences and format the text.
70
        FormattingPreferences prefs = new FormattingPreferences();
70
        FormattingPreferences prefs = new FormattingPreferences();
71
        
71
        
72
        NonParsingXMLFormatter formatter = new NonParsingXMLFormatter();     
72
        XmlDocumentFormatter formatter = new XmlDocumentFormatter();     
73
        formatter.setText(documentText);
73
        formatter.setText(documentText);
74
        formatter.setFormattingPreferences(prefs);
74
        formatter.setFormattingPreferences(prefs);
75
        
75
        
(-)Ant Editor/org/eclipse/ant/internal/ui/editor/formatter/XmlElementFormattingStrategy.java (-153 / +28 lines)
Lines 12-21 Link Here
12
12
13
package org.eclipse.ant.internal.ui.editor.formatter;
13
package org.eclipse.ant.internal.ui.editor.formatter;
14
14
15
import java.text.StringCharacterIterator;
16
import java.util.ArrayList;
17
import java.util.LinkedList;
15
import java.util.LinkedList;
18
import java.util.List;
19
16
20
import org.eclipse.jface.text.Assert;
17
import org.eclipse.jface.text.Assert;
21
import org.eclipse.jface.text.BadLocationException;
18
import org.eclipse.jface.text.BadLocationException;
Lines 28-34 Link Here
28
import org.eclipse.jface.text.formatter.IFormattingContext;
25
import org.eclipse.jface.text.formatter.IFormattingContext;
29
import org.eclipse.jface.text.source.ISourceViewer;
26
import org.eclipse.jface.text.source.ISourceViewer;
30
27
31
public class XmlElementFormattingStrategy extends ContextBasedFormattingStrategy {
28
public class XmlElementFormattingStrategy extends
29
        ContextBasedFormattingStrategy {
32
30
33
    /** Indentations to use by this strategy */
31
    /** Indentations to use by this strategy */
34
    private final LinkedList fIndentations = new LinkedList();
32
    private final LinkedList fIndentations = new LinkedList();
Lines 39-52 Link Here
39
    /** The position sets to keep track of during formatting */
37
    /** The position sets to keep track of during formatting */
40
    private final LinkedList fPositions = new LinkedList();
38
    private final LinkedList fPositions = new LinkedList();
41
39
42
    /** access to the preferences store **/
40
    /** access to the preferences store * */
43
    private final FormattingPreferences prefs = new FormattingPreferences();
41
    private final FormattingPreferences prefs = new FormattingPreferences();
44
    
42
45
    public XmlElementFormattingStrategy(ISourceViewer viewer) {
43
    public XmlElementFormattingStrategy(ISourceViewer viewer) {
46
        super(viewer);
44
        super(viewer);
47
    }
45
    }
48
46
49
    /* (non-Javadoc)
47
    /*
48
     * (non-Javadoc)
49
     * 
50
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#format()
50
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#format()
51
     */
51
     */
52
    public void format() {
52
    public void format() {
Lines 64-217 Link Here
64
64
65
            String formatted = formatElement(document, partition, lineIndent);
65
            String formatted = formatElement(document, partition, lineIndent);
66
66
67
            String partitionText = document.get(partition.getOffset(), partition.getLength());
67
            String partitionText = document.get(partition.getOffset(),
68
                    partition.getLength());
68
69
69
            if (formatted != null && !formatted.equals(partitionText)) {
70
            if (formatted != null && !formatted.equals(partitionText)) {
70
            	document.replace(partition.getOffset(), partition.getLength(), formatted);
71
                document.replace(partition.getOffset(), partition.getLength(),
72
                        formatted);
71
            }
73
            }
72
74
73
        } catch (BadLocationException e) {
75
        } catch (BadLocationException e) {
74
        }
76
        }
75
    }
77
    }
76
78
77
    private String formatElement(IDocument document, TypedPosition partition, String indentation) throws BadLocationException {
79
private String formatElement(IDocument document, TypedPosition partition,
78
80
            String indentation) throws BadLocationException {
79
        String partitionText = document.get(partition.getOffset(), partition.getLength());
80
81
        StringBuffer formattedElement = null;
82
83
        // do we even need to think about wrapping?
84
        
85
		// TODO fix this. If wrapping is all that this strategy does, this is a
86
		// very inefficient mechanism for avoiding the element format since this
87
        // format method will be called for every tag partition in the document.
88
        // What is really needed is for the editor itself to listen for changes 
89
        // to the preferences and reconfigure the editor's formatter to ommit 
90
        // this strategy.
91
        
92
        // TODO Always parse and create a model for the element since we may 
93
        // want to undo the formatting of a previously formatted element when
94
        // the prefences change
95
96
        if (prefs.useElementWrapping() && !partitionText.startsWith("</")) { //$NON-NLS-1$
97
98
            IRegion line = document.getLineInformationOfOffset(partition.getOffset());
99
100
            int partitionLineOffset = partition.getOffset() - line.getOffset();
101
102
            // do we have a good candidate for a wrap?
103
            // chars need to be expanded using the preferences value           
104
            int tabCount = count('\t', document.get(line.getOffset(), line.getLength()));
105
                        
106
            if ((line.getLength() - tabCount) + (tabCount * prefs.getTabWidth())  
107
            		> prefs.getMaximumLineWidth()) {
108
109
                List attributes = getAttributes(partitionText);
110
                if (attributes.size() > 1) {
111
                    formattedElement = new StringBuffer();
112
                    String startTag = elementStart(partitionText);
113
                    formattedElement.append(startTag);
114
                    formattedElement.append(' ');
115
                    formattedElement.append(attributes.get(0));
116
                    
117
                    for (int i = 1; i < attributes.size(); i++) {
118
                    	formattedElement.append("\n"); //$NON-NLS-1$
119
                        formattedElement.append(indentation);
120
                        for (int j = 0; j < (partitionLineOffset - indentation
121
                                .length())
122
                                + startTag.length() + 1; j++) {
123
                            formattedElement.append(' ');
124
                        }
125
                        formattedElement.append(attributes.get(i));                        
126
                    }
127
                    
128
					if (prefs.alignElementCloseChar()) {
129
						formattedElement.append("\n"); //$NON-NLS-1$
130
						formattedElement.append(indentation);
131
						for (int j = 0; j < (partitionLineOffset - indentation
132
								.length()) + 1; j++) {
133
							formattedElement.append(' ');
134
						}			
135
					}
136
					
137
                    if (partitionText.endsWith("/>")) { //$NON-NLS-1$
138
                        formattedElement.append("/>"); //$NON-NLS-1$
139
                    } else if (partitionText.endsWith(">")) { //$NON-NLS-1$
140
                        formattedElement.append(">"); //$NON-NLS-1$
141
                    } else {
142
                        Assert.isTrue(false, "Bad Partitioner."); //$NON-NLS-1$
143
                    }
144
                }
145
            }
146
        }
147
        return formattedElement != null ? formattedElement.toString() : null;
148
    }
149
81
150
    private List getAttributes(String text) {
82
        String partitionText = document.get(partition.getOffset(), partition
83
                .getLength());
151
84
152
        List attributes = new ArrayList();
85
        IRegion line = document.getLineInformationOfOffset(partition
86
                .getOffset());
153
87
154
        int start = firstWhitespaceIn(text);
88
        FormattingPreferences prefs = new FormattingPreferences();
155
        if (start == -1) {
156
        	return attributes;
157
        }
158
        boolean insideQuotes = false;
159
89
160
        boolean haveEquals = false;
90
        int indentLength = partition.getOffset() - line.getOffset();
161
        int quotes = 0;
162
        StringBuffer attributePair = new StringBuffer();
163
164
        // TODO logic for inside quotes incorrectly assumes that the quote
165
        // character will be " when it could also be '.
166
        for (int i = start; i < text.length(); i++) {
167
            char c = text.charAt(i);
168
            switch (c) {
169
            case '"':
170
                insideQuotes = !insideQuotes;
171
                quotes++;
172
                attributePair.append(c);
173
                if (!insideQuotes && haveEquals && quotes == 2) {
174
                    // we're done with this attribute
175
                    attributes.add(attributePair.toString());
176
                    // reset
177
                    attributePair = new StringBuffer();
178
                    quotes = 0;
179
                    haveEquals = false;
180
                }
181
                break;
182
            case '=':
183
                attributePair.append(c);
184
                haveEquals = true;
185
                break;
186
            default:
187
                if (Character.isWhitespace(c) && !insideQuotes) {
188
                    if (!Character.isWhitespace(text.charAt(i - 1))
189
                            && attributePair.length() != 0) {
190
                        attributePair.append(' ');
191
                    }
192
                } else {
193
                    attributePair.append(c);
194
                }
195
                break;
196
            }
197
        }
198
        return attributes;
199
    }
200
91
201
    private String elementStart(String text) {
92
        return new XmlTagFormatter().format(partitionText, prefs, document.get(line.getOffset(),
202
        return text.substring(0, firstWhitespaceIn(text));
93
                indentLength));
203
    }
204
94
205
    private int firstWhitespaceIn(String text) {
206
        for (int i = 0; i < text.length(); i++) {
207
            if (Character.isWhitespace(text.charAt(i))) { 
208
            	return i; 
209
            }
210
        }
211
        return -1;
212
    }
95
    }
213
96
214
    /* (non-Javadoc)
97
    /*
98
     * (non-Javadoc)
99
     * 
215
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStarts(org.eclipse.jface.text.formatter.IFormattingContext)
100
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStarts(org.eclipse.jface.text.formatter.IFormattingContext)
216
     */
101
     */
217
    public void formatterStarts(IFormattingContext context) {
102
    public void formatterStarts(IFormattingContext context) {
Lines 228-234 Link Here
228
113
229
    }
114
    }
230
115
231
    /* (non-Javadoc)
116
    /*
117
     * (non-Javadoc)
118
     * 
232
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStops()
119
     * @see org.eclipse.jface.text.formatter.IFormattingStrategyExtension#formatterStops()
233
     */
120
     */
234
    public void formatterStops() {
121
    public void formatterStops() {
Lines 239-254 Link Here
239
        fPositions.clear();
126
        fPositions.clear();
240
    }
127
    }
241
128
242
	private int count(char searchChar, String inTargetString) {
129
}
243
		StringCharacterIterator iter = new StringCharacterIterator(
244
				inTargetString);
245
		int i = 0;
246
		if(iter.first() == searchChar) i++;
247
		while (iter.getIndex() < iter.getEndIndex()) {			
248
			if (iter.next() == searchChar) {
249
				i++;
250
			}
251
		}
252
		return i;
253
	}
254
}
(-)Ant (+46 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 John-Mason P. Shackelford 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
 *     John-Mason P. Shackelford - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.ant.internal.ui.editor.formatter;
12
13
import java.util.MissingResourceException;
14
import java.util.ResourceBundle;
15
16
/**
17
 *
18
 */
19
public class Messages {
20
21
    private static final String BUNDLE_NAME = "org.eclipse.ant.internal.ui.editor.formatter.test";//$NON-NLS-1$
22
23
    private static final ResourceBundle RESOURCE_BUNDLE = ResourceBundle
24
            .getBundle(BUNDLE_NAME);
25
26
    /**
27
     * 
28
     */
29
    private Messages() {
30
31
        // TODO Auto-generated constructor stub
32
    }
33
34
    /**
35
     * @param key
36
     * @return
37
     */
38
    public static String getString(String key) {
39
        // TODO Auto-generated method stub
40
        try {
41
            return RESOURCE_BUNDLE.getString(key);
42
        } catch (MissingResourceException e) {
43
            return '!' + key + '!';
44
        }
45
    }
46
}
(-)Ant (+410 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 John-Mason P. Shackelford 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
 *     John-Mason P. Shackelford - initial API and implementation
10
 * 	   IBM Corporation - bug 52076
11
 *******************************************************************************/
12
13
package org.eclipse.ant.internal.ui.editor.formatter;
14
15
import java.io.IOException;
16
import java.io.Reader;
17
import java.io.StringReader;
18
19
import org.eclipse.ant.internal.ui.model.AntUIPlugin;
20
import org.eclipse.jface.text.Assert;
21
22
public class XmlDocumentFormatter {
23
24
    private static class CommentReader extends TagReader {
25
26
        private boolean complete = false;
27
28
        protected void clear() {
29
            this.complete = false;
30
        }
31
32
        public String getStartOfTag() {
33
            return "<!--"; //$NON-NLS-1$
34
        }
35
36
        protected String readTag() throws IOException {
37
            int intChar;
38
            char c;
39
            StringBuffer node = new StringBuffer();
40
41
            while (!complete && (intChar = reader.read()) != -1) {
42
                c = (char) intChar;
43
44
                node.append(c);
45
46
                if (c == '>' && node.toString().endsWith("-->")) { //$NON-NLS-1$
47
                    complete = true;
48
                }
49
            }
50
            return node.toString();
51
        }
52
    }
53
54
    private static class DoctypeDeclarationReader extends TagReader {
55
56
        private boolean complete = false;
57
58
        protected void clear() {
59
            this.complete = false;
60
        }
61
62
        public String getStartOfTag() {
63
            return "<!"; //$NON-NLS-1$
64
        }
65
66
        protected String readTag() throws IOException {
67
            int intChar;
68
            char c;
69
            StringBuffer node = new StringBuffer();
70
71
            while (!complete && (intChar = reader.read()) != -1) {
72
                c = (char) intChar;
73
74
                node.append(c);
75
76
                if (c == '>') {
77
                    complete = true;
78
                }
79
            }
80
            return node.toString();
81
        }
82
83
    }
84
85
    private static class ProcessingInstructionReader extends TagReader {
86
87
        private boolean complete = false;
88
89
        protected void clear() {
90
            this.complete = false;
91
        }
92
93
        public String getStartOfTag() {
94
            return "<?"; //$NON-NLS-1$
95
        }
96
97
        protected String readTag() throws IOException {
98
            int intChar;
99
            char c;
100
            StringBuffer node = new StringBuffer();
101
102
            while (!complete && (intChar = reader.read()) != -1) {
103
                c = (char) intChar;
104
105
                node.append(c);
106
107
                if (c == '>' && node.toString().endsWith("?>")) { //$NON-NLS-1$
108
                    complete = true;
109
                }
110
            }
111
            return node.toString();
112
        }
113
    }
114
115
    private static abstract class TagReader {
116
117
        protected Reader reader;
118
119
        private String tagText;
120
121
        protected abstract void clear();
122
123
        public int getPostTagDepthModifier() {
124
            return 0;
125
        }
126
127
        public int getPreTagDepthModifier() {
128
            return 0;
129
        }
130
131
        abstract public String getStartOfTag();
132
133
        public String getTagText() {
134
            return this.tagText;
135
        }
136
137
        public boolean isTextNode() {
138
            return false;
139
        }
140
141
        protected abstract String readTag() throws IOException;
142
143
        public boolean requiresInitialIndent() {
144
            return true;
145
        }
146
147
        public void setReader(Reader reader) throws IOException {
148
            this.reader = reader;
149
            this.clear();
150
            this.tagText = readTag();
151
        }
152
153
        public boolean startsOnNewline() {
154
            return true;
155
        }
156
    }
157
158
    private static class TagReaderFactory {
159
160
        // Warning: the order of the Array is important!
161
        private static TagReader[] tagReaders = new TagReader[]{new CommentReader(),
162
                                                                new DoctypeDeclarationReader(),
163
                                                                new ProcessingInstructionReader(),
164
                                                                new XmlElementReader()};
165
166
        private static TagReader textNodeReader = new TextReader();
167
168
        public static TagReader createTagReaderFor(Reader reader)
169
                                                                 throws IOException {
170
171
            char[] buf = new char[10];
172
            reader.mark(10);
173
            reader.read(buf, 0, 10);
174
            reader.reset();
175
176
            String startOfTag = String.valueOf(buf);
177
178
            for (int i = 0; i < tagReaders.length; i++) {
179
                if (startOfTag.startsWith(tagReaders[i].getStartOfTag())) {
180
                    tagReaders[i].setReader(reader);
181
                    return tagReaders[i];
182
                }
183
            }
184
            // else
185
            textNodeReader.setReader(reader);
186
            return textNodeReader;
187
        }
188
    }
189
190
    private static class TextReader extends TagReader {
191
192
        private boolean complete;
193
194
        private boolean isTextNode;
195
196
        protected void clear() {
197
            this.complete = false;
198
        }
199
200
        /* (non-Javadoc)
201
         * @see org.eclipse.ant.internal.ui.editor.formatter.XmlDocumentFormatter.TagReader#getStartOfTag()
202
         */
203
        public String getStartOfTag() {
204
            return ""; //$NON-NLS-1$
205
        }
206
207
        /* (non-Javadoc)
208
         * @see org.eclipse.ant.internal.ui.editor.formatter.XmlDocumentFormatter.TagReader#isTextNode()
209
         */
210
        public boolean isTextNode() {
211
            return this.isTextNode;
212
        }
213
214
        protected String readTag() throws IOException {
215
216
            StringBuffer node = new StringBuffer();
217
218
            while (!complete) {
219
220
                reader.mark(1);
221
                int intChar = reader.read();
222
                if (intChar == -1) break;
223
224
                char c = (char) intChar;
225
                if (c == '<') {
226
                    reader.reset();
227
                    complete = true;
228
                } else {
229
                    node.append(c);
230
                }
231
            }
232
233
            // if this text node is just whitespace
234
            // remove it, except for the newlines.
235
            if (node.length() < 1) {
236
                this.isTextNode = false;
237
238
            } else if (node.toString().trim().length() == 0) {
239
                String whitespace = node.toString();
240
                node = new StringBuffer();
241
                for (int i = 0; i < whitespace.length(); i++) {
242
                    char whitespaceCharacter = whitespace.charAt(i);
243
                    if (whitespaceCharacter == '\n')
244
                            node.append(whitespaceCharacter);
245
                }
246
                this.isTextNode = false;
247
248
            } else {
249
                this.isTextNode = true;
250
            }
251
            return node.toString();
252
        }
253
254
        /* (non-Javadoc)
255
         * @see org.eclipse.ant.internal.ui.editor.formatter.XmlDocumentFormatter.TagReader#requiresInitialIndent()
256
         */
257
        public boolean requiresInitialIndent() {
258
            return false;
259
        }
260
261
        /* (non-Javadoc)
262
         * @see org.eclipse.ant.internal.ui.editor.formatter.XmlDocumentFormatter.TagReader#startsOnNewline()
263
         */
264
        public boolean startsOnNewline() {
265
            return false;
266
        }
267
    }
268
269
    private static class XmlElementReader extends TagReader {
270
271
        private boolean complete = false;
272
273
        protected void clear() {
274
            this.complete = false;
275
        }
276
277
        public int getPostTagDepthModifier() {
278
            if (getTagText().endsWith("/>") || getTagText().endsWith("/ >")) { //$NON-NLS-1$ //$NON-NLS-2$
279
                return 0;
280
            } else if (getTagText().startsWith("</")) { //$NON-NLS-1$
281
                return 0;
282
            } else {
283
                return +1;
284
            }
285
        }
286
287
        public int getPreTagDepthModifier() {
288
            if (getTagText().startsWith("</")) { //$NON-NLS-1$
289
                return -1;
290
            } else {
291
                return 0;
292
            }
293
        }
294
295
        public String getStartOfTag() {
296
            return "<"; //$NON-NLS-1$
297
        }
298
299
        protected String readTag() throws IOException {
300
301
            StringBuffer node = new StringBuffer();
302
303
            boolean insideQuote = false;
304
            int intChar;
305
306
            while (!complete && (intChar = reader.read()) != -1) {
307
                char c = (char) intChar;
308
309
                node.append(c);
310
                // TODO logic incorrectly assumes that " is quote character
311
                // when it could also be '
312
                if (c == '"') {
313
                    insideQuote = !insideQuote;
314
                }
315
                if (c == '>' && !insideQuote) {
316
                    complete = true;
317
                }
318
            }
319
            return node.toString();
320
        }
321
    }
322
323
    private int depth;
324
325
    private String documentText;
326
327
    private StringBuffer formattedXml;
328
329
    private boolean lastNodeWasText;
330
331
    private FormattingPreferences prefs;
332
333
    private void copyNode(Reader reader, StringBuffer out) throws IOException {
334
335
        TagReader tag = TagReaderFactory.createTagReaderFor(reader);
336
337
        depth = depth + tag.getPreTagDepthModifier();
338
339
        if (!lastNodeWasText) {
340
341
            if (tag.startsOnNewline() && !hasNewlineAlready(out)) {
342
                out.append("\n"); //$NON-NLS-1$
343
            }
344
345
            if (tag.requiresInitialIndent()) {
346
                out.append(indent());
347
            }
348
        }
349
350
        out.append(tag.getTagText());
351
352
        depth = depth + tag.getPostTagDepthModifier();
353
354
        lastNodeWasText = tag.isTextNode();
355
356
    }
357
358
    /**
359
     * @return
360
     */
361
    public String format() {
362
363
        Assert.isNotNull(this.documentText);
364
        Assert.isNotNull(this.prefs);
365
366
        Reader reader = new StringReader(documentText);
367
        formattedXml = new StringBuffer();
368
369
        depth = 0;
370
        lastNodeWasText = false;
371
        try {
372
            while (true) {
373
                reader.mark(1);
374
                int intChar = reader.read();
375
                reader.reset();
376
377
                if (intChar != -1) {
378
                    copyNode(reader, formattedXml);
379
                } else {
380
                    break;
381
                }
382
            }
383
            reader.close();
384
        } catch (IOException e) {
385
           AntUIPlugin.log(e);
386
        }
387
        return formattedXml.toString();
388
    }
389
390
    private boolean hasNewlineAlready(StringBuffer out) {
391
        return out.lastIndexOf("\n") == formattedXml.length() - 1 //$NON-NLS-1$
392
               || out.lastIndexOf("\r") == formattedXml.length() - 1; //$NON-NLS-1$
393
    }
394
395
    private String indent() {
396
        StringBuffer indent = new StringBuffer(30);
397
        for (int i = 0; i < depth; i++) {
398
            indent.append(prefs.getCanonicalIndent());
399
        }
400
        return indent.toString();
401
    }
402
403
    public void setFormattingPreferences(FormattingPreferences prefs) {
404
        this.prefs = prefs;
405
    }
406
407
    public void setText(String documentText) {
408
        this.documentText = documentText;
409
    }
410
}
(-)Ant (+450 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2004 John-Mason P. Shackelford 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
 *     John-Mason P. Shackelford - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.ant.internal.ui.editor.formatter;
12
13
import java.text.CharacterIterator;
14
import java.text.StringCharacterIterator;
15
import java.util.ArrayList;
16
import java.util.Arrays;
17
import java.util.List;
18
19
/**
20
 *  
21
 */
22
public class XmlTagFormatter {
23
24
    protected static class AttributePair {
25
26
        private String attribute;
27
28
        private String value;
29
30
        public AttributePair(String attribute, String value) {
31
            this.attribute = attribute;
32
            this.value = value;
33
        }
34
35
        public String getAttribute() {
36
            return attribute;
37
        }
38
39
        public String getValue() {
40
            return value;
41
        }
42
    }
43
44
    protected static class ParseException extends Exception {
45
46
        public ParseException(String message) {
47
            super(message);
48
        }
49
    }
50
51
    protected static class Tag {
52
53
        private List attributes = new ArrayList();
54
55
        private boolean closed;
56
57
        private String elementName;
58
59
        public void addAttribute(String attribute, String value) {
60
            attributes.add(new AttributePair(attribute, value));
61
        }
62
63
        public int attributeCount() {
64
            return attributes.size();
65
        }
66
67
        public AttributePair getAttributePair(int i) {
68
            return (AttributePair) attributes.get(i);
69
        }
70
71
        public String getElementName() {
72
            return this.elementName;
73
        }
74
75
        public boolean isClosed() {
76
            return closed;
77
        }
78
79
        public int minimumLength() {
80
            int length = 2; // for the < >
81
            if (this.isClosed()) length++; // if we need to add an />
82
            length += this.getElementName().length();
83
            if (this.attributeCount() > 0 || this.isClosed()) length++;
84
            for (int i = 0; i < this.attributeCount(); i++) {
85
                AttributePair attributePair = this.getAttributePair(i);
86
                length += attributePair.getAttribute().length();
87
                length += attributePair.getValue().length();
88
                length += 4; // equals sign, quote characters & trailing space
89
            }
90
            if (this.attributeCount() > 0 && !this.isClosed()) length--;
91
            return length;
92
        }
93
94
        public void setAttributes(List attributePair) {
95
            attributes.clear();
96
            attributes.addAll(attributePair);
97
        }
98
99
        public void setClosed(boolean closed) {
100
            this.closed = closed;
101
        }
102
103
        public void setElementName(String elementName) {
104
            this.elementName = elementName;
105
        }
106
107
        public String toString() {
108
            StringBuffer sb = new StringBuffer(500);
109
            sb.append("<"); //$NON-NLS-1$
110
            sb.append(this.getElementName());
111
            if (this.attributeCount() > 0 || this.isClosed()) sb.append(' ');
112
113
            for (int i = 0; i < this.attributeCount(); i++) {
114
                AttributePair attributePair = this.getAttributePair(i);
115
                sb.append(attributePair.getAttribute());
116
                sb.append("=\""); //$NON-NLS-1$
117
                sb.append(attributePair.getValue());
118
                sb.append("\""); //$NON-NLS-1$
119
                if (this.isClosed() || i != this.attributeCount() - 1)
120
                        sb.append(' ');
121
            }
122
            if (this.isClosed()) sb.append("/"); //$NON-NLS-1$
123
            sb.append(">"); //$NON-NLS-1$
124
            return sb.toString();
125
        }
126
    }
127
128
    protected static class TagFormatter {
129
130
        /**
131
         * @param searchChar
132
         * @param inTargetString
133
         * @return
134
         */
135
        private int countChar(char searchChar, String inTargetString) {
136
            StringCharacterIterator iter = new StringCharacterIterator(
137
                    inTargetString);
138
            int i = 0;
139
            if (iter.first() == searchChar) i++;
140
            while (iter.getIndex() < iter.getEndIndex()) {
141
                if (iter.next() == searchChar) {
142
                    i++;
143
                }
144
            }
145
            return i;
146
        }
147
148
        /**
149
         * @param tagText
150
         * @param prefs
151
         * @param indent
152
         * @return
153
         */
154
        public String format(Tag tag, FormattingPreferences prefs, String indent) {
155
            if (prefs.wrapLongTags()
156
                    && lineRequiresWrap(indent + tag.toString(), prefs
157
                            .getMaximumLineWidth(), prefs.getTabWidth())) {
158
                return wrapTag(tag, prefs, indent);
159
            } else {
160
                return tag.toString();
161
            }
162
        }
163
164
        /**
165
         * @param line
166
         * @param lineWidth
167
         * @param tabWidth
168
         * @return
169
         */
170
        protected boolean lineRequiresWrap(String line, int lineWidth,
171
                int tabWidth) {
172
            return tabExpandedLineWidth(line, tabWidth) > lineWidth;
173
        }
174
175
        /**
176
         * @param line
177
         *            the line in which spaces are to be expanded
178
         * @param tabWidth
179
         *            number of spaces to substitute for a tab
180
         * @return length of the line with tabs expanded to spaces
181
         */
182
        protected int tabExpandedLineWidth(String line, int tabWidth) {
183
            int tabCount = countChar('\t', line);
184
            return (line.length() - tabCount) + (tabCount * tabWidth);
185
        }
186
187
        /**
188
         * @param tag
189
         * @param prefs
190
         * @param indent
191
         * @return
192
         */
193
        protected String wrapTag(Tag tag, FormattingPreferences prefs,
194
                String indent) {
195
            StringBuffer sb = new StringBuffer(1024);
196
            sb.append('<');
197
            sb.append(tag.getElementName());
198
            sb.append(' ');
199
200
            if (tag.attributeCount() > 0) {
201
                sb.append(tag.getAttributePair(0).getAttribute());
202
                sb.append("=\""); //$NON-NLS-1$
203
                sb.append(tag.getAttributePair(0).getValue());
204
                sb.append('"');
205
            }
206
207
            if (tag.attributeCount() > 1) {
208
                char[] extraIndent = new char[tag.getElementName().length() + 2];
209
                Arrays.fill(extraIndent, ' ');
210
                for (int i = 1; i < tag.attributeCount(); i++) {
211
                    sb.append('\n');
212
                    sb.append(indent);
213
                    sb.append(extraIndent);
214
                    sb.append(tag.getAttributePair(i).getAttribute());
215
                    sb.append("=\""); //$NON-NLS-1$
216
                    sb.append(tag.getAttributePair(i).getValue());
217
                    sb.append('"');
218
                }
219
            }
220
221
            if (prefs.alignElementCloseChar()) {
222
                sb.append("\n"); //$NON-NLS-1$
223
                sb.append(indent);
224
            } else if (tag.isClosed()) {
225
                sb.append(' ');
226
            }
227
228
            if (tag.isClosed()) sb.append("/"); //$NON-NLS-1$
229
            sb.append(">"); //$NON-NLS-1$
230
            return sb.toString();
231
        }
232
    }
233
234
    // if object creation is an issue, use static methods or a flyweight
235
    // pattern
236
    protected static class TagParser {
237
238
        private String elementName;
239
240
        private String parseText;
241
242
        /**
243
         *  
244
         */
245
        protected List getAttibutes(String elementText)
246
                throws ParseException {
247
248
            class Mode {
249
                private int mode;
250
                public void setAttributeNameSearching() {mode = 0;}
251
                public void setAttributeNameFound() {mode = 1;}
252
                public void setAttributeValueSearching() {mode = 2;}
253
                public void setAttributeValueFound() {mode = 3;}
254
                public void setFinished() {mode = 4;}
255
                public boolean isAttributeNameSearching() {return mode == 0;}
256
                public boolean isAttributeNameFound() {return mode == 1;}
257
                public boolean isAttributeValueSearching() {return mode == 2;}
258
                public boolean isAttributeValueFound() {return mode == 3;}
259
                public boolean isFinished() {return mode == 4;}
260
            }
261
262
            List attributePairs = new ArrayList();
263
264
            CharacterIterator iter = new StringCharacterIterator(elementText
265
                    .substring(getElementName(elementText).length() + 2));
266
267
            // state for finding attributes
268
            Mode mode = new Mode();
269
            mode.setAttributeNameSearching();
270
            char attributeQuote = '"';
271
            StringBuffer currentAttributeName = null;
272
            StringBuffer currentAttributeValue = null;
273
274
            char c = iter.first();
275
            while (!mode.isFinished() && iter.getIndex() < iter.getEndIndex()) {
276
                
277
                switch (c) {                
278
                
279
                case '"':
280
                case '\'':
281
282
                    if (mode.isAttributeValueSearching()) {
283
284
                        // start of an attribute value
285
                        attributeQuote = c;
286
                        mode.setAttributeValueFound();
287
                        currentAttributeValue = new StringBuffer(1024);
288
289
                    } else if (mode.isAttributeValueFound()
290
                            && attributeQuote == c) {
291
292
                        // we've completed a pair!
293
                        AttributePair pair = new AttributePair(
294
                                currentAttributeName.toString(),
295
                                currentAttributeValue.toString());
296
297
                        attributePairs.add(pair);
298
299
                        // start looking for another attribute
300
                        mode.setAttributeNameSearching();
301
302
                    } else if (mode.isAttributeValueFound()
303
                            && attributeQuote != c) {
304
305
                        // this quote character is part of the attribute value
306
                        currentAttributeValue.append(c);
307
308
                    } else {
309
                        // this is no place for a quote!
310
                        throw new ParseException("Unexpected '" + c //$NON-NLS-1$
311
                                + "' when parsing:\n\t" + elementText); //$NON-NLS-1$
312
                    }
313
                    break;
314
315
                case '=':
316
                    
317
                    if (mode.isAttributeValueFound()) {
318
319
                        // this character is part of the attribute value
320
                        currentAttributeValue.append(c);
321
322
                    } else if (mode.isAttributeNameFound()) {
323
324
                        // end of the name, now start looking for the value
325
                        mode.setAttributeValueSearching();
326
                        
327
                    } else {
328
                        // this is no place for an equals sign!
329
                        throw new ParseException("Unexpected '" + c //$NON-NLS-1$
330
                                + "' when parsing:\n\t" + elementText); //$NON-NLS-1$
331
                    }
332
                    break;
333
334
                case '/':
335
                case '>':
336
                    if (mode.isAttributeValueFound()) {
337
                        // attribute values are CDATA, add it all
338
                        currentAttributeValue.append(c);
339
                    } else if (mode.isAttributeNameSearching()) {
340
                        mode.setFinished();
341
                    } else {
342
                        // we aren't ready to be done!
343
                        throw new ParseException("Unexpected '" + c //$NON-NLS-1$
344
                                + "' when parsing:\n\t" + elementText); //$NON-NLS-1$
345
                    }
346
                    break;
347
348
                default:
349
350
                    if (mode.isAttributeValueFound()) {
351
                        // attribute values are CDATA, add it all
352
                        currentAttributeValue.append(c);
353
                    } else {
354
                        if (!Character.isWhitespace(c)) {
355
                            if (mode.isAttributeNameSearching()) {
356
                                // we found the start of an attribute name
357
                                mode.setAttributeNameFound();
358
                                currentAttributeName = new StringBuffer(255);
359
                                currentAttributeName.append(c);
360
                            } else if (mode.isAttributeNameFound()) {
361
                                currentAttributeName.append(c);                            
362
                            }
363
                        }
364
                    }
365
                    break;
366
                }
367
                
368
                c = iter.next();
369
            }
370
            return attributePairs;
371
        }
372
373
        /**
374
         * @param tagText
375
         *            text of an XML tag
376
         * @return extracted XML element name
377
         */
378
        protected String getElementName(String tagText) throws ParseException {
379
            if (!tagText.equals(this.parseText) || this.elementName == null) {
380
                int endOfTag = tagEnd(tagText);
381
                if ((tagText.length() > 2) && (endOfTag > 1)) {
382
                    this.parseText = tagText;
383
                    this.elementName = tagText.substring(1, endOfTag);
384
                } else {
385
                    throw new ParseException("No element name for the tag:\n\t" //$NON-NLS-1$
386
                            + tagText);
387
                }
388
            }
389
            return elementName;
390
        }
391
392
        /**
393
         * @param tagText
394
         * @return
395
         */
396
        protected boolean isClosed(String tagText) {
397
            return tagText.charAt(tagText.lastIndexOf(">") - 1) == '/'; //$NON-NLS-1$
398
        }
399
400
        /**
401
         * @param tagText
402
         * @return an fully populated tag
403
         */
404
        public Tag parse(String tagText) throws ParseException {
405
            Tag tag = new Tag();
406
            tag.setElementName(getElementName(tagText));
407
            tag.setAttributes(getAttibutes(tagText));
408
            tag.setClosed(isClosed(tagText));
409
            return tag;
410
        }
411
412
        private int tagEnd(String text) {
413
            // This is admittedly a little loose, but we don't want the
414
            // formatter to be too strict...
415
            // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name
416
            for (int i = 1; i < text.length(); i++) {
417
                char c = text.charAt(i);
418
                if (!Character.isLetterOrDigit(c) && c != ':' && c != '.'
419
                        && c != '-' && c != '_') { return i; }
420
            }
421
            return -1;
422
        }
423
424
    }
425
426
    /**
427
     * @param tagText
428
     * @param prefs
429
     * @param indent
430
     * @return
431
     */
432
    public String format(String tagText, FormattingPreferences prefs,
433
            String indent) {
434
435
        Tag tag;
436
        if (tagText.startsWith("</") || tagText.startsWith("<%")
437
                || tagText.startsWith("<?") || tagText.startsWith("<[")) {
438
            return tagText;
439
        } else {
440
            try {
441
                tag = new TagParser().parse(tagText);
442
            } catch (ParseException e) {
443
                // if we can't parse the tag, give up and leave the text as is.
444
                return tagText;
445
            }
446
            return new TagFormatter().format(tag, prefs, indent);
447
        }
448
    }
449
450
}

Return to bug 40255