1 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.AntUIPlugin; 20 import org.eclipse.ant.internal.ui.preferences.AntEditorPreferenceConstants; 21 import org.eclipse.core.runtime.Assert; 22 import org.eclipse.jface.preference.IPreferenceStore; 23 import org.eclipse.jface.text.BadLocationException; 24 import org.eclipse.jface.text.IDocument; 25 import org.eclipse.jface.text.IRegion; 26 27 public class XmlDocumentFormatter { 28 29 private static class CommentReader extends TagReader { 30 31 private boolean complete = false; 32 33 protected void clear() { 34 this.complete = false; 35 } 36 37 public String getStartOfTag() { 38 return "<!--"; } 40 41 protected String readTag() throws IOException { 42 int intChar; 43 char c; 44 StringBuffer node = new StringBuffer (); 45 46 while (!complete && (intChar = reader.read()) != -1) { 47 c = (char) intChar; 48 49 node.append(c); 50 51 if (c == '>' && node.toString().endsWith("-->")) { complete = true; 53 } 54 } 55 return node.toString(); 56 } 57 } 58 59 private static class DoctypeDeclarationReader extends TagReader { 60 61 private boolean complete = false; 62 63 protected void clear() { 64 this.complete = false; 65 } 66 67 public String getStartOfTag() { 68 return "<!"; } 70 71 protected String readTag() throws IOException { 72 int intChar; 73 char c; 74 StringBuffer node = new StringBuffer (); 75 76 while (!complete && (intChar = reader.read()) != -1) { 77 c = (char) intChar; 78 79 node.append(c); 80 81 if (c == '>') { 82 complete = true; 83 } 84 } 85 return node.toString(); 86 } 87 88 } 89 90 private static class ProcessingInstructionReader extends TagReader { 91 92 private boolean complete = false; 93 94 protected void clear() { 95 this.complete = false; 96 } 97 98 public String getStartOfTag() { 99 return "<?"; } 101 102 protected String readTag() throws IOException { 103 int intChar; 104 char c; 105 StringBuffer node = new StringBuffer (); 106 107 while (!complete && (intChar = reader.read()) != -1) { 108 c = (char) intChar; 109 110 node.append(c); 111 112 if (c == '>' && node.toString().endsWith("?>")) { complete = true; 114 } 115 } 116 return node.toString(); 117 } 118 } 119 120 private static abstract class TagReader { 121 122 protected Reader reader; 123 124 private String tagText; 125 126 protected abstract void clear(); 127 128 public int getPostTagDepthModifier() { 129 return 0; 130 } 131 132 public int getPreTagDepthModifier() { 133 return 0; 134 } 135 136 abstract public String getStartOfTag(); 137 138 public String getTagText() { 139 return this.tagText; 140 } 141 142 public boolean isTextNode() { 143 return false; 144 } 145 146 protected abstract String readTag() throws IOException ; 147 148 public boolean requiresInitialIndent() { 149 return true; 150 } 151 152 public void setReader(Reader reader) throws IOException { 153 this.reader = reader; 154 this.clear(); 155 this.tagText = readTag(); 156 } 157 158 public boolean startsOnNewline() { 159 return true; 160 } 161 } 162 163 private static class TagReaderFactory { 164 165 private static TagReader[] tagReaders = new TagReader[]{new CommentReader(), 167 new DoctypeDeclarationReader(), 168 new ProcessingInstructionReader(), 169 new XmlElementReader()}; 170 171 private static TagReader textNodeReader = new TextReader(); 172 173 public static TagReader createTagReaderFor(Reader reader) 174 throws IOException { 175 176 char[] buf = new char[10]; 177 reader.mark(10); 178 reader.read(buf, 0, 10); 179 reader.reset(); 180 181 String startOfTag = String.valueOf(buf); 182 183 for (int i = 0; i < tagReaders.length; i++) { 184 if (startOfTag.startsWith(tagReaders[i].getStartOfTag())) { 185 tagReaders[i].setReader(reader); 186 return tagReaders[i]; 187 } 188 } 189 textNodeReader.setReader(reader); 191 return textNodeReader; 192 } 193 } 194 195 private static class TextReader extends TagReader { 196 197 private boolean complete; 198 199 private boolean isTextNode; 200 201 protected void clear() { 202 this.complete = false; 203 } 204 205 208 public String getStartOfTag() { 209 return ""; } 211 212 215 public boolean isTextNode() { 216 return this.isTextNode; 217 } 218 219 protected String readTag() throws IOException { 220 221 StringBuffer node = new StringBuffer (); 222 223 while (!complete) { 224 225 reader.mark(1); 226 int intChar = reader.read(); 227 if (intChar == -1) break; 228 229 char c = (char) intChar; 230 if (c == '<') { 231 reader.reset(); 232 complete = true; 233 } else { 234 node.append(c); 235 } 236 } 237 238 if (node.length() < 1) { 241 this.isTextNode = false; 242 243 } else if (node.toString().trim().length() == 0) { 244 String whitespace = node.toString(); 245 node = new StringBuffer (); 246 for (int i = 0; i < whitespace.length(); i++) { 247 char whitespaceCharacter = whitespace.charAt(i); 248 if (whitespaceCharacter == '\n' || whitespaceCharacter == '\r') { 249 node.append(whitespaceCharacter); 250 } 251 } 252 this.isTextNode = false; 253 254 } else { 255 this.isTextNode = true; 256 } 257 return node.toString(); 258 } 259 260 263 public boolean requiresInitialIndent() { 264 return false; 265 } 266 267 270 public boolean startsOnNewline() { 271 return false; 272 } 273 } 274 275 private static class XmlElementReader extends TagReader { 276 277 private boolean complete = false; 278 279 protected void clear() { 280 this.complete = false; 281 } 282 283 public int getPostTagDepthModifier() { 284 if (getTagText().endsWith("/>") || getTagText().endsWith("/ >")) { return 0; 286 } else if (getTagText().startsWith("</")) { return 0; 288 } else { 289 return +1; 290 } 291 } 292 293 public int getPreTagDepthModifier() { 294 if (getTagText().startsWith("</")) { return -1; 296 } 297 return 0; 298 } 299 300 public String getStartOfTag() { 301 return "<"; } 303 304 protected String readTag() throws IOException { 305 306 StringBuffer node = new StringBuffer (); 307 308 boolean insideQuote = false; 309 int intChar; 310 311 while (!complete && (intChar = reader.read()) != -1) { 312 char c = (char) intChar; 313 314 node.append(c); 315 if (c == '"') { 318 insideQuote = !insideQuote; 319 } 320 if (c == '>' && !insideQuote) { 321 complete = true; 322 } 323 } 324 return node.toString(); 325 } 326 } 327 328 private int depth; 329 private StringBuffer formattedXml; 330 private boolean lastNodeWasText; 331 private String fDefaultLineDelimiter; 332 333 public XmlDocumentFormatter() { 334 super(); 335 depth= -1; 336 } 337 338 private void copyNode(Reader reader, StringBuffer out, FormattingPreferences prefs) throws IOException { 339 340 TagReader tag = TagReaderFactory.createTagReaderFor(reader); 341 342 depth = depth + tag.getPreTagDepthModifier(); 343 344 if (!lastNodeWasText) { 345 346 if (tag.startsOnNewline() && !hasNewlineAlready(out)) { 347 out.append(fDefaultLineDelimiter); 348 } 349 350 if (tag.requiresInitialIndent()) { 351 out.append(indent(prefs.getCanonicalIndent())); 352 } 353 } 354 355 out.append(tag.getTagText()); 356 357 depth = depth + tag.getPostTagDepthModifier(); 358 359 lastNodeWasText = tag.isTextNode(); 360 361 } 362 363 369 public static int computeIndent(String line, int tabWidth) { 370 int result= 0; 371 int blanks= 0; 372 int size= line.length(); 373 for (int i= 0; i < size; i++) { 374 char c= line.charAt(i); 375 if (c == '\t') { 376 result++; 377 blanks= 0; 378 } else if (isIndentChar(c)) { 379 blanks++; 380 if (blanks == tabWidth) { 381 result++; 382 blanks= 0; 383 } 384 } else { 385 return result; 386 } 387 } 388 return result; 389 } 390 391 395 public static boolean isIndentChar(char ch) { 396 return Character.isWhitespace(ch) && !isLineDelimiterChar(ch); 397 } 398 399 402 public static boolean isLineDelimiterChar(char ch) { 403 return ch == '\n' || ch == '\r'; 404 } 405 406 public String format(String documentText, FormattingPreferences prefs) { 407 408 Assert.isNotNull(documentText); 409 Assert.isNotNull(prefs); 410 411 Reader reader = new StringReader (documentText); 412 formattedXml = new StringBuffer (); 413 414 if (depth == -1) { 415 depth = 0; 416 } 417 lastNodeWasText = false; 418 try { 419 while (true) { 420 reader.mark(1); 421 int intChar = reader.read(); 422 reader.reset(); 423 424 if (intChar != -1) { 425 copyNode(reader, formattedXml, prefs); 426 } else { 427 break; 428 } 429 } 430 reader.close(); 431 } catch (IOException e) { 432 AntUIPlugin.log(e); 433 } 434 return formattedXml.toString(); 435 } 436 437 private boolean hasNewlineAlready(StringBuffer out) { 438 return out.lastIndexOf("\n") == formattedXml.length() - 1 || out.lastIndexOf("\r") == formattedXml.length() - 1; } 441 442 private String indent(String canonicalIndent) { 443 StringBuffer indent = new StringBuffer (30); 444 for (int i = 0; i < depth; i++) { 445 indent.append(canonicalIndent); 446 } 447 return indent.toString(); 448 } 449 450 public void setInitialIndent(int indent) { 451 depth= indent; 452 } 453 454 463 public static StringBuffer getLeadingWhitespace(int offset, IDocument document) { 464 StringBuffer indent= new StringBuffer (); 465 try { 466 IRegion line= document.getLineInformationOfOffset(offset); 467 int lineOffset= line.getOffset(); 468 int nonWS= findEndOfWhiteSpace(document, lineOffset, lineOffset + line.getLength()); 469 indent.append(document.get(lineOffset, nonWS - lineOffset)); 470 return indent; 471 } catch (BadLocationException e) { 472 return indent; 473 } 474 } 475 476 487 public static int findEndOfWhiteSpace(IDocument document, int offset, int end) throws BadLocationException { 488 while (offset < end) { 489 char c= document.getChar(offset); 490 if (c != ' ' && c != '\t') { 491 return offset; 492 } 493 offset++; 494 } 495 return end; 496 } 497 498 504 public static StringBuffer createIndent() { 505 StringBuffer oneIndent= new StringBuffer (); 506 IPreferenceStore pluginPrefs= AntUIPlugin.getDefault().getPreferenceStore(); 507 pluginPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_TAB_CHAR); 508 509 if (!pluginPrefs.getBoolean(AntEditorPreferenceConstants.FORMATTER_TAB_CHAR)) { 510 int tabLen= pluginPrefs.getInt(AntEditorPreferenceConstants.FORMATTER_TAB_SIZE); 511 for (int i= 0; i < tabLen; i++) { 512 oneIndent.append(' '); 513 } 514 } else { 515 oneIndent.append('\t'); } 517 518 return oneIndent; 519 } 520 521 public void setDefaultLineDelimiter(String defaultLineDelimiter) { 522 fDefaultLineDelimiter= defaultLineDelimiter; 523 524 } 525 } | Popular Tags |