1 12 package org.eclipse.ant.internal.ui.editor.formatter; 13 14 import java.text.CharacterIterator ; 15 import java.text.StringCharacterIterator ; 16 import java.util.ArrayList ; 17 import java.util.Arrays ; 18 import java.util.List ; 19 20 public class XmlTagFormatter { 21 22 protected static class AttributePair { 23 24 private String fAttribute; 25 private String fValue; 26 private char fQuote; 27 28 public AttributePair(String attribute, String value, char attributeQuote) { 29 fAttribute = attribute; 30 fValue = value; 31 fQuote= attributeQuote; 32 } 33 34 public String getAttribute() { 35 return fAttribute; 36 } 37 38 public String getValue() { 39 return fValue; 40 } 41 42 public char getQuote() { 43 return fQuote; 44 } 45 } 46 47 protected static class ParseException extends Exception { 48 49 private static final long serialVersionUID = 1L; 50 51 public ParseException(String message) { 52 super(message); 53 } 54 } 55 56 protected static class Tag { 57 58 private List fAttributes = new ArrayList (); 59 60 private boolean fClosed; 61 62 private String fElementName; 63 64 public void addAttribute(String attribute, String value, char quote) { 65 fAttributes.add(new AttributePair(attribute, value, quote)); 66 } 67 68 public int attributeCount() { 69 return fAttributes.size(); 70 } 71 72 public AttributePair getAttributePair(int i) { 73 return (AttributePair) fAttributes.get(i); 74 } 75 76 public String getElementName() { 77 return this.fElementName; 78 } 79 80 public boolean isClosed() { 81 return fClosed; 82 } 83 84 public int minimumLength() { 85 int length = 2; if (this.isClosed()) length++; length += this.getElementName().length(); 88 if (this.attributeCount() > 0 || this.isClosed()) length++; 89 for (int i = 0; i < this.attributeCount(); i++) { 90 AttributePair attributePair = this.getAttributePair(i); 91 length += attributePair.getAttribute().length(); 92 length += attributePair.getValue().length(); 93 length += 4; } 95 if (this.attributeCount() > 0 && !this.isClosed()) length--; 96 return length; 97 } 98 99 public void setAttributes(List attributePair) { 100 fAttributes.clear(); 101 fAttributes.addAll(attributePair); 102 } 103 104 public void setClosed(boolean closed) { 105 fClosed = closed; 106 } 107 108 public void setElementName(String elementName) { 109 fElementName = elementName; 110 } 111 112 public String toString() { 113 StringBuffer sb = new StringBuffer (500); 114 sb.append('<'); 115 sb.append(this.getElementName()); 116 if (this.attributeCount() > 0 || this.isClosed()) sb.append(' '); 117 118 for (int i = 0; i < this.attributeCount(); i++) { 119 AttributePair attributePair = this.getAttributePair(i); 120 sb.append(attributePair.getAttribute()); 121 sb.append('='); 122 sb.append(attributePair.getQuote()); 123 sb.append(attributePair.getValue()); 124 sb.append(attributePair.getQuote()); 125 if (this.isClosed() || i != this.attributeCount() - 1) 126 sb.append(' '); 127 } 128 if (this.isClosed()) sb.append('/'); 129 sb.append('>'); 130 return sb.toString(); 131 } 132 } 133 134 protected static class TagFormatter { 135 136 private int countChar(char searchChar, String inTargetString) { 137 StringCharacterIterator iter = new StringCharacterIterator ( 138 inTargetString); 139 int i = 0; 140 if (iter.first() == searchChar) i++; 141 while (iter.getIndex() < iter.getEndIndex()) { 142 if (iter.next() == searchChar) { 143 i++; 144 } 145 } 146 return i; 147 } 148 149 public String format(Tag tag, FormattingPreferences prefs, String indent, String lineDelimiter) { 150 if (prefs.wrapLongTags() 151 && lineRequiresWrap(indent + tag.toString(), prefs 152 .getMaximumLineWidth(), prefs.getTabWidth())) { 153 return wrapTag(tag, prefs, indent, lineDelimiter); 154 } 155 return tag.toString(); 156 } 157 158 protected boolean lineRequiresWrap(String line, int lineWidth, int tabWidth) { 159 return tabExpandedLineWidth(line, tabWidth) > lineWidth; 160 } 161 162 169 protected int tabExpandedLineWidth(String line, int tabWidth) { 170 int tabCount = countChar('\t', line); 171 return (line.length() - tabCount) + (tabCount * tabWidth); 172 } 173 174 protected String wrapTag(Tag tag, FormattingPreferences prefs, String indent, String lineDelimiter) { 175 StringBuffer sb = new StringBuffer (1024); 176 sb.append('<'); 177 sb.append(tag.getElementName()); 178 sb.append(' '); 179 180 if (tag.attributeCount() > 0) { 181 AttributePair pair= tag.getAttributePair(0); 182 sb.append(pair.getAttribute()); 183 sb.append('='); 184 sb.append(pair.getQuote()); 185 sb.append(tag.getAttributePair(0).getValue()); 186 sb.append(pair.getQuote()); 187 } 188 189 if (tag.attributeCount() > 1) { 190 char[] extraIndent = new char[tag.getElementName().length() + 2]; 191 Arrays.fill(extraIndent, ' '); 192 for (int i = 1; i < tag.attributeCount(); i++) { 193 AttributePair pair= tag.getAttributePair(i); 194 sb.append(lineDelimiter); 195 sb.append(indent); 196 sb.append(extraIndent); 197 sb.append(pair.getAttribute()); 198 sb.append('='); 199 sb.append(pair.getQuote()); 200 sb.append(pair.getValue()); 201 sb.append(pair.getQuote()); 202 } 203 } 204 205 if (prefs.alignElementCloseChar()) { 206 sb.append(lineDelimiter); 207 sb.append(indent); 208 } else if (tag.isClosed()) { 209 sb.append(' '); 210 } 211 212 if (tag.isClosed()) sb.append('/'); 213 sb.append('>'); 214 return sb.toString(); 215 } 216 } 217 218 protected static class TagParser { 221 222 private String fElementName; 223 224 private String fParseText; 225 226 protected List getAttibutes(String elementText) 227 throws ParseException { 228 229 class Mode { 230 private int mode; 231 public void setAttributeNameSearching() {mode = 0;} 232 public void setAttributeNameFound() {mode = 1;} 233 public void setAttributeValueSearching() {mode = 2;} 234 public void setAttributeValueFound() {mode = 3;} 235 public void setFinished() {mode = 4;} 236 public boolean isAttributeNameSearching() {return mode == 0;} 237 public boolean isAttributeNameFound() {return mode == 1;} 238 public boolean isAttributeValueSearching() {return mode == 2;} 239 public boolean isAttributeValueFound() {return mode == 3;} 240 public boolean isFinished() {return mode == 4;} 241 } 242 243 List attributePairs = new ArrayList (); 244 245 CharacterIterator iter = new StringCharacterIterator (elementText 246 .substring(getElementName(elementText).length() + 2)); 247 248 Mode mode = new Mode(); 250 mode.setAttributeNameSearching(); 251 char attributeQuote = '"'; 252 StringBuffer currentAttributeName = null; 253 StringBuffer currentAttributeValue = null; 254 255 char c = iter.first(); 256 while (iter.getIndex() < iter.getEndIndex()) { 257 258 switch (c) { 259 260 case '"': 261 case '\'': 262 263 if (mode.isAttributeValueSearching()) { 264 265 attributeQuote = c; 267 mode.setAttributeValueFound(); 268 currentAttributeValue = new StringBuffer (1024); 269 270 } else if (mode.isAttributeValueFound() 271 && attributeQuote == c) { 272 273 AttributePair pair = new AttributePair( 275 currentAttributeName.toString(), 276 currentAttributeValue.toString(), attributeQuote); 277 278 attributePairs.add(pair); 279 280 mode.setAttributeNameSearching(); 282 283 } else if (mode.isAttributeValueFound() 284 && attributeQuote != c) { 285 286 currentAttributeValue.append(c); 288 289 } else { 290 throw new ParseException("Unexpected '" + c + "' when parsing:\n\t" + elementText); } 294 break; 295 296 case '=': 297 298 if (mode.isAttributeValueFound()) { 299 300 currentAttributeValue.append(c); 302 303 } else if (mode.isAttributeNameFound()) { 304 305 mode.setAttributeValueSearching(); 307 308 } else { 309 throw new ParseException("Unexpected '" + c + "' when parsing:\n\t" + elementText); } 313 break; 314 315 case '/': 316 case '>': 317 if (mode.isAttributeValueFound()) { 318 currentAttributeValue.append(c); 320 } else if (mode.isAttributeNameSearching()) { 321 mode.setFinished(); 322 } else if (mode.isFinished()){ 323 } else { 325 throw new ParseException("Unexpected '" + c + "' when parsing:\n\t" + elementText); } 329 break; 330 331 default: 332 333 if (mode.isAttributeValueFound()) { 334 currentAttributeValue.append(c); 336 337 } else if (mode.isFinished()) { 338 if (!Character.isWhitespace(c)) { 339 throw new ParseException("Unexpected '" + c + "' when parsing:\n\t" + elementText); } 342 } else { 343 if (!Character.isWhitespace(c)) { 344 if (mode.isAttributeNameSearching()) { 345 mode.setAttributeNameFound(); 347 currentAttributeName = new StringBuffer (255); 348 currentAttributeName.append(c); 349 } else if (mode.isAttributeNameFound()) { 350 currentAttributeName.append(c); 351 } 352 } 353 } 354 break; 355 } 356 357 c = iter.next(); 358 } 359 if (!mode.isFinished()) { 360 throw new ParseException("Element did not complete normally."); } 362 return attributePairs; 363 } 364 365 370 protected String getElementName(String tagText) throws ParseException { 371 if (!tagText.equals(this.fParseText) || this.fElementName == null) { 372 int endOfTag = tagEnd(tagText); 373 if ((tagText.length() > 2) && (endOfTag > 1)) { 374 this.fParseText = tagText; 375 this.fElementName = tagText.substring(1, endOfTag); 376 } else { 377 throw new ParseException("No element name for the tag:\n\t" + tagText); 379 } 380 } 381 return fElementName; 382 } 383 384 protected boolean isClosed(String tagText) { 385 return tagText.charAt(tagText.lastIndexOf('>') - 1) == '/'; 386 } 387 388 392 public Tag parse(String tagText) throws ParseException { 393 Tag tag = new Tag(); 394 tag.setElementName(getElementName(tagText)); 395 tag.setAttributes(getAttibutes(tagText)); 396 tag.setClosed(isClosed(tagText)); 397 return tag; 398 } 399 400 private int tagEnd(String text) { 401 for (int i = 1; i < text.length(); i++) { 405 char c = text.charAt(i); 406 if (!Character.isLetterOrDigit(c) && c != ':' && c != '.' 407 && c != '-' && c != '_') { return i; } 408 } 409 return -1; 410 } 411 } 412 413 public static String format(String tagText, FormattingPreferences prefs, String indent, String lineDelimiter) { 414 415 Tag tag; 416 if (tagText.startsWith("</") || tagText.startsWith("<%") || tagText.startsWith("<?") || tagText.startsWith("<[")) { return tagText; 419 } 420 try { 421 tag = new TagParser().parse(tagText); 422 } catch (ParseException e) { 423 return tagText; 425 } 426 return new TagFormatter().format(tag, prefs, indent, lineDelimiter); 427 } 428 } | Popular Tags |