View Javadoc
1   /*
2    * REPOWEB, repository manager.
3    *
4    * Terms of license - http://opensource.org/licenses/apachepl.php
5    */
6   package org.repoweb.xml;
7   import java.util.regex.Matcher;
8   import java.util.regex.Pattern;
9   /***
10   * Simple XML Beautifier. This XML Beautifier doesn't need
11   * to have a valid xml file, since it doesn't use any XML API.
12   */
13  class XmlBeautifier {
14      public String beautify(String xml) {
15          if (null == xml) {
16              return null;
17          }
18          XmlHandler handler = new XmlHandler();
19          XmlParser parser = new XmlParser(handler);
20          parser.parse(xml);
21          return handler.result();
22      }
23  
24      /***
25       * Beautify XML.
26       */
27      private static class XmlHandler {
28          private StringBuffer _buffer = new StringBuffer();
29          private final String _newLine = System.getProperty("line.separator");
30          private int _numberOfIndent = 0;
31          private String _oneIndent = "   ";
32          private boolean _hadText = false;
33          private Pattern _whitespacePattern = Pattern.compile("//s*");
34  
35          public void startElement(String raw) {
36              indentIfNeeded();
37              addIndent();
38              _buffer.append(raw);
39          }
40  
41  
42          public void endElement(String raw) {
43              removeIndent();
44              indentIfNeeded();
45              _buffer.append(raw);
46          }
47  
48  
49          public void characters(String raw) {
50              Matcher matcher = _whitespacePattern.matcher(raw);
51              if (!matcher.matches()) {
52                  _buffer.append(raw.trim());
53                  _hadText = true;
54              }
55          }
56  
57  
58          public void comment(String raw) {
59              _buffer.append(_newLine);
60              indent();
61              _buffer.append(raw);
62          }
63  
64  
65          public String result() {
66              return _buffer.toString();
67          }
68  
69  
70          private void indent() {
71              for (int i = 0; i < _numberOfIndent; i++) {
72                  _buffer.append(_oneIndent);
73              }
74          }
75  
76  
77          private void indentIfNeeded() {
78              if (_hadText) {
79                  _hadText = false;
80              }
81              else {
82                  if (_buffer.length() != 0) {
83                      _buffer.append(_newLine);
84                  }
85                  indent();
86              }
87          }
88  
89  
90          private void addIndent() {
91              _numberOfIndent++;
92          }
93  
94  
95          private void removeIndent() {
96              _numberOfIndent--;
97              _numberOfIndent = (_numberOfIndent < 0) ? 0 : _numberOfIndent;
98          }
99      }
100 
101     /***
102      * Parse XML string into event.
103      */
104     private static class XmlParser {
105         private static final int IN_TAG = 0;
106         private static final int IN_CLOSING_TAG = 1;
107         private static final int CONTENT = 2;
108         private static final int IN_COMMENT = 3;
109         private final XmlHandler _handler;
110         private int _first = 0;
111         private int _last = 0;
112         private int _state = IN_TAG;
113         private int _prevTagState = IN_TAG;
114 
115         public XmlParser(XmlHandler handler) {
116             _handler = handler;
117         }
118 
119         public void parse(final String xml) {
120             char prevCh = ' ';
121             for (int i = 0; i < xml.length(); i++) {
122                 char ch = xml.charAt(i);
123                 _prevTagState = _state;
124 
125                 if (_state == IN_COMMENT) {
126                     if (ch == '>' && prevCh == '-') {
127                         _state = CONTENT;
128                     }
129                 }
130                 else if (ch == '<') {
131                     _state = IN_TAG;
132                 }
133                 else if (ch == '>') {
134                     _state = CONTENT;
135                 }
136                 else if (_state == IN_TAG && ch == '!') {
137                     _state = IN_COMMENT;
138                 }
139                 else if (_state == IN_TAG && ch == '/') {
140                     _state = IN_CLOSING_TAG;
141                 }
142 
143                 prevCh = ch;
144                 if (_state != _prevTagState
145                         && !(_prevTagState == IN_TAG
146                         && _state == IN_CLOSING_TAG)
147                         && !(_prevTagState == IN_TAG
148                         && _state == IN_COMMENT)) {
149 //                    System.out.println("\t changement de " + str(_prevTagState)
150 //                            + " en " + str(_state) + "par char[" + i + "] = " + ch + " - " + xml.substring(i));
151                     _last = i;
152                     handleStateChange(xml);
153                     _first = _last;
154                 }
155             }
156         }
157 
158 
159 //        private String str(int state) {
160 //            switch (state) {
161 //                case IN_TAG:
162 //                    return "IN_TAG";
163 //                case IN_CLOSING_TAG:
164 //                    return "IN_CLOSING_TAG";
165 //                case IN_COMMENT:
166 //                    return "IN_COMMENT";
167 //                case CONTENT:
168 //                    return "CONTENT";
169 //                default:
170 //                    return "N/A";
171 //            }
172 //        }
173         private void handleStateChange(final String xml) {
174             switch (_prevTagState) {
175                 case IN_TAG:
176                     _handler.startElement(xml.substring(_first, _last + 1));
177                     break;
178                 case IN_CLOSING_TAG:
179                     _handler.endElement(xml.substring(_first, _last + 1));
180                     break;
181                 case IN_COMMENT:
182                     _handler.comment(xml.substring(_first, _last + 1));
183                     break;
184                 case CONTENT:
185                     _handler.characters(xml.substring(_first + 1, _last));
186                     break;
187                 default:
188                     throw new IllegalStateException("Unknown State " + _prevTagState);
189             }
190         }
191     }
192 }