1 package org.apache.maven.shared.utils.xml;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.shared.utils.io.IOUtil;
23 import org.apache.maven.shared.utils.xml.pull.XmlPullParserException;
24
25 import org.xml.sax.*;
26 import org.xml.sax.helpers.DefaultHandler;
27
28 import javax.annotation.Nonnull;
29 import javax.annotation.WillClose;
30 import java.io.*;
31 import java.util.ArrayList;
32 import java.util.List;
33
34
35
36
37 public class Xpp3DomBuilder
38 {
39 private static final boolean DEFAULT_TRIM = true;
40
41 public static Xpp3Dom build( @WillClose @Nonnull Reader reader )
42 throws XmlPullParserException
43 {
44 return build( reader, DEFAULT_TRIM );
45 }
46
47 public static Xpp3Dom build( @WillClose InputStream is, @Nonnull String encoding )
48 throws XmlPullParserException
49 {
50 return build( is, encoding, DEFAULT_TRIM );
51 }
52
53 public static Xpp3Dom build( @WillClose InputStream is, @Nonnull String encoding, boolean trim )
54 throws XmlPullParserException
55 {
56 try
57 {
58 Reader reader = new InputStreamReader( is, encoding );
59 return build( reader, trim );
60 }
61 catch ( UnsupportedEncodingException e )
62 {
63 throw new RuntimeException( e );
64 }
65 }
66
67 public static Xpp3Dom build( @WillClose Reader reader, boolean trim )
68 throws XmlPullParserException
69 {
70 try
71 {
72 DocHandler docHandler = parseSax( new InputSource( reader ), trim );
73 return docHandler.result;
74 }
75 finally
76 {
77 IOUtil.close( reader );
78 }
79 }
80
81 private static DocHandler parseSax( @Nonnull InputSource inputSource, boolean trim )
82 throws XmlPullParserException
83 {
84
85 try
86 {
87 DocHandler ch = new DocHandler( trim );
88 XMLReader parser = org.xml.sax.helpers.XMLReaderFactory.createXMLReader();
89 parser.setContentHandler( ch );
90 parser.parse( inputSource );
91 return ch;
92 }
93 catch ( IOException e )
94 {
95 throw new XmlPullParserException( e );
96 }
97 catch ( SAXException e )
98 {
99 throw new XmlPullParserException( e );
100 }
101 }
102
103 private static class DocHandler
104 extends DefaultHandler
105 {
106 private final List<Xpp3Dom> elemStack = new ArrayList<Xpp3Dom>();
107
108 private final List<StringBuilder> values = new ArrayList<StringBuilder>();
109
110
111 private final List<SAXParseException> warnings = new ArrayList<SAXParseException>();
112 private final List<SAXParseException> errors = new ArrayList<SAXParseException>();
113 private final List<SAXParseException> fatals = new ArrayList<SAXParseException>();
114
115
116 Xpp3Dom result = null;
117
118 private final boolean trim;
119
120 private boolean spacePreserve = false;
121
122 DocHandler( boolean trim )
123 {
124 this.trim = trim;
125 }
126
127 @Override
128 public void startElement( String uri, String localName, String qName, Attributes attributes )
129 throws SAXException
130 {
131 spacePreserve = false;
132 Xpp3Dom child = new Xpp3Dom( localName );
133
134 attachToParent( child );
135 pushOnStack( child );
136
137
138
139 values.add( new StringBuilder() );
140
141 int size = attributes.getLength();
142 for ( int i = 0; i < size; i++ )
143 {
144 String name = attributes.getQName( i );
145 String value = attributes.getValue( i );
146 child.setAttribute( name, value );
147 spacePreserve = spacePreserve || ( "xml:space".equals( name ) && "preserve".equals( value ) );
148 }
149 }
150
151 private boolean pushOnStack( Xpp3Dom child )
152 {
153 return elemStack.add( child );
154 }
155
156 private void attachToParent( Xpp3Dom child )
157 {
158 int depth = elemStack.size();
159 if ( depth > 0 )
160 {
161 elemStack.get( depth - 1 ).addChild( child );
162 }
163 }
164
165 @Override
166 public void warning( SAXParseException e )
167 throws SAXException
168 {
169 warnings.add( e );
170 }
171
172 @Override
173 public void error( SAXParseException e )
174 throws SAXException
175 {
176 errors.add( e );
177 }
178
179 @Override
180 public void fatalError( SAXParseException e )
181 throws SAXException
182 {
183 fatals.add( e );
184 }
185
186 private Xpp3Dom pop()
187 {
188 int depth = elemStack.size() - 1;
189 return elemStack.remove( depth );
190 }
191
192 @Override
193 public void endElement( String uri, String localName, String qName )
194 throws SAXException
195 {
196 int depth = elemStack.size() - 1;
197
198 Xpp3Dom element = pop();
199
200
201 Object accumulatedValue = values.remove( depth );
202
203 if ( element.getChildCount() == 0 )
204 {
205 if ( accumulatedValue == null )
206 {
207 element.setValue( "" );
208 }
209 else
210 {
211 element.setValue( accumulatedValue.toString() );
212 }
213 }
214
215 if ( depth == 0 )
216 {
217 result = element;
218 }
219 }
220
221 @Override
222 public void characters( char[] ch, int start, int length )
223 throws SAXException
224 {
225 String text = new String( ch, start, length );
226 appendToTopValue( ( trim && !spacePreserve ) ? text.trim() : text );
227 }
228
229 private void appendToTopValue( String toAppend )
230 {
231
232 StringBuilder stringBuilder = values.get( values.size() - 1 );
233 stringBuilder.append( toAppend );
234 }
235 }
236
237 }