Source code: com/port80/eclipse/xml/editors/XMLAutoIndentStrategy.java
1 package com.port80.eclipse.xml.editors;
2
3 import org.eclipse.jface.text.BadLocationException;
4 import org.eclipse.jface.text.IDocument;
5 import org.eclipse.jface.text.ITypedRegion;
6 import org.eclipse.jface.text.source.ISourceViewer;
7
8 import com.port80.eclipse.editors.EditorsPlugin;
9 import com.port80.eclipse.editors.EditorsUtil;
10 import com.port80.eclipse.editors.IEditorConfiguration;
11 import com.port80.eclipse.editors.util.AbstractXMLAutoIndentStrategy;
12
13 /**
14 * Perform XML auto-indent.
15 *
16 * @author chrisl
17 */
18 public class XMLAutoIndentStrategy extends AbstractXMLAutoIndentStrategy {
19
20 ////////////////////////////////////////////////////////////////////////
21
22 private static final String NAME = "XMLAutoIndentStrategy";
23
24 /** Preformatted partitions. No tag counting, simple indent strategy. */
25 public static final String[] PREFORMATTED_PARTITIONS =
26 new String[] { IEditorConfiguration.CDATA_PARTITION, IEditorConfiguration.PROC_INST_PARTITION };
27
28 /** Escaped partitions. No tag counting. */
29 public static final String[] ESCAPED_PARTITIONS =
30 new String[] {
31 IEditorConfiguration.CDATA_PARTITION,
32 IEditorConfiguration.PROC_INST_PARTITION,
33 IEditorConfiguration.COMMENT_PARTITION };
34
35 public static final String[] CDATA_PARTITIONS = new String[] { IEditorConfiguration.CDATA_PARTITION };
36
37 ////////////////////////////////////////////////////////////////////////
38
39 /**
40 * Constructor for XMLAutoIndentStrategy.
41 */
42 public XMLAutoIndentStrategy(XMLEditorConfiguration cf, ISourceViewer viewer) {
43 super(cf, viewer);
44 }
45
46 ////////////////////////////////////////////////////////////////////////
47
48 protected String[] getEscapedPartitions() {
49 return ESCAPED_PARTITIONS;
50 }
51
52 protected String[] getPreformattedPartitions() {
53 return PREFORMATTED_PARTITIONS;
54 }
55
56 protected boolean isInsideEscapedPartitions(int offset, IDocument doc) {
57 if (EditorsUtil.isInsidePartition(offset, CDATA_PARTITIONS, true, doc))
58 return true;
59 return EditorsUtil.isInsidePartition(offset, ESCAPED_PARTITIONS, doc);
60 }
61
62 protected boolean isInsidePreformattedPartitions(int offset, IDocument doc) {
63 if (EditorsUtil.isInsidePartition(offset, CDATA_PARTITIONS, true, doc))
64 return true;
65 return EditorsUtil.isInsidePartition(offset, PREFORMATTED_PARTITIONS, doc);
66 }
67
68 /**
69 * @return Offset of the open angle bracket of the matching open tag before the given offset 'end'.
70 * Assuming there is a close tag at 'end'.
71 */
72 protected int findMatchingOpenTagBefore(int end, IDocument doc) {
73 int level = 0;
74 ITypedRegion partition;
75 try {
76 char c;
77 char prev = 0;
78 for (--end; end >= 0; --end) {
79 if (EditorsUtil.isInsidePartition(end, CDATA_PARTITIONS, true, doc)) {
80 partition = doc.getPartition(end);
81 end = partition.getOffset() - 1;
82 } else if (EditorsUtil.isInsidePartition(end, ESCAPED_PARTITIONS, doc)) {
83 partition = doc.getPartition(end);
84 end = partition.getOffset();
85 }
86 c = doc.getChar(end);
87 if (c == '/') {
88 if (prev == '>') {
89 // self end.
90 end = findOpenBracketBefore(end, doc);
91 c = '<';
92 }
93 } else if (c == '<') {
94 if (prev == '/') {
95 ++level;
96 } else if (prev == '?' || prev == '!') {
97 } else if (level == 0) {
98 return end;
99 } else {
100 --level;
101 }
102 } else if (c == '"' || c == '\'') {
103 end = getStringStart(doc, end, c);
104 }
105 prev = c;
106 }
107 } catch (BadLocationException e) {
108 EditorsPlugin.log(EditorsPlugin.getResourceString("bad_location.message"), 1, e); //$NON-NLS-1$
109 }
110 return -1;
111 }
112
113 /**
114 * Count number of open tags-close tags in the given range.
115 * NOTE: No white space in char. sequences '</' and '/>' allowed here.
116 * @param skip True to ignore the first leading end tag.
117 */
118 protected int getTagCount(IDocument document, int start, int end, boolean skip) throws BadLocationException {
119 int bracketcount = 0;
120 char c, c1;
121 ITypedRegion partition;
122 while (start < end) {
123 if (EditorsUtil.isInsidePartition(start, ESCAPED_PARTITIONS, true, document)) {
124 partition = document.getPartition(start);
125 start = partition.getOffset() + partition.getLength();
126 }
127 c = document.getChar(start);
128 start++;
129 switch (c) {
130 case '/' :
131 if (start < end) {
132 c1 = document.getChar(start);
133 if (c1 == '>') {
134 // Self ended tag.
135 skip = false;
136 --bracketcount;
137 ++start;
138 }
139 }
140 break;
141 // case '-' :
142 // if (start < end - 1) {
143 // c1 = document.getChar(start);
144 // c2 = document.getChar(start + 1);
145 // if (c1 == '-' && c2 == '>') {
146 // // end of comment.
147 // if (!skip)
148 // --bracketcount;
149 // skip = false;
150 // start += 2;
151 // }
152 // }
153 // break;
154 // case ']' :
155 // if (start < end - 1) {
156 // c1 = document.getChar(start);
157 // c2 = document.getChar(start + 1);
158 // if (c1 == ']' && c2 == '>') {
159 // // end of CDATA
160 // if (!skip)
161 // --bracketcount;
162 // skip = false;
163 // start += 2;
164 // }
165 // }
166 // break;
167 case '<' :
168 if (start < end) {
169 c1 = document.getChar(start);
170 if (c1 == '?' || c1 == '!') {
171 // Process instruction, comment or CDATA no end tag.
172 skip = false;
173 } else if (c1 == '/') {
174 // End tag.
175 if (!skip)
176 --bracketcount;
177 skip = false;
178 ++start;
179 } else {
180 // start tag
181 ++bracketcount;
182 skip = false;
183 }
184 }
185 break;
186 case '"' :
187 case '\'' :
188 start = getStringEnd(document, start, end, c);
189 break;
190 }
191 }
192 return bracketcount;
193 }
194
195 }