1 package org.apache.maven.scm.provider.accurev.cli;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.IOException;
23 import java.io.Reader;
24 import java.io.Writer;
25 import java.nio.channels.Channels;
26 import java.nio.channels.Pipe;
27 import java.nio.channels.Pipe.SinkChannel;
28 import java.nio.channels.Pipe.SourceChannel;
29 import java.nio.charset.Charset;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Map;
35
36 import org.apache.maven.scm.log.ScmLogger;
37 import org.codehaus.plexus.util.cli.StreamConsumer;
38 import org.codehaus.plexus.util.xml.pull.MXParser;
39 import org.codehaus.plexus.util.xml.pull.XmlPullParser;
40 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
41
42
43
44
45
46
47 public abstract class XppStreamConsumer
48 extends Thread
49 implements StreamConsumer
50
51 {
52 public ScmLogger getLogger()
53 {
54 return logger;
55 }
56
57 private Writer writer;
58
59 private XmlPullParser parser = new MXParser();
60
61 private volatile boolean complete = false;
62
63 private ScmLogger logger;
64
65 private int lineCount = 0;
66
67 private Reader reader;
68
69 public XppStreamConsumer( ScmLogger logger )
70 {
71
72 super();
73 this.logger = logger;
74 try
75 {
76 Pipe p = Pipe.open();
77 SinkChannel sink = p.sink();
78 SourceChannel source = p.source();
79 writer = Channels.newWriter( sink, Charset.defaultCharset().name() );
80 reader = Channels.newReader( source, Charset.defaultCharset().name() );
81 parser.setInput( reader );
82
83 }
84 catch ( Exception e )
85 {
86 logger.error( "Exception initialising pipe", e );
87 }
88
89 }
90
91 public final void consumeLine( String line )
92 {
93
94
95 try
96 {
97 writer.append( line );
98 if ( lineCount == 0 )
99 {
100 this.start();
101 }
102 lineCount++;
103 writer.flush();
104 }
105 catch ( IOException e )
106 {
107 throw new RuntimeException( "error pumping line to pipe", e );
108 }
109
110 }
111
112 @Override
113 public void run()
114 {
115
116 try
117 {
118 parse( parser );
119 }
120 catch ( Exception e )
121 {
122 caughtParseException( e );
123 }
124 finally
125 {
126 synchronized ( this )
127 {
128
129 try
130 {
131 reader.close();
132 }
133 catch ( IOException e )
134 {
135 getLogger().warn( "Error closing pipe reader", e );
136 }
137
138 complete = true;
139 this.notifyAll();
140 }
141 }
142 }
143
144 protected void caughtParseException( Exception e )
145 {
146 logger.warn( "Exception parsing input", e );
147
148 }
149
150 protected void parse( XmlPullParser p )
151 throws XmlPullParserException, IOException
152 {
153 List<String> tagPath = new ArrayList<String>();
154 int eventType = p.getEventType();
155 if ( logger.isDebugEnabled() )
156 {
157 logger.debug( "Event " + eventType );
158 }
159
160 while ( eventType != XmlPullParser.END_DOCUMENT )
161 {
162 int lastIndex = tagPath.size() - 1;
163 String tagName;
164 switch ( eventType )
165 {
166 case XmlPullParser.START_DOCUMENT:
167
168 break;
169
170 case XmlPullParser.START_TAG:
171 tagName = p.getName();
172 if ( tagName != null )
173 {
174 tagPath.add( tagName );
175 int attributeCount = p.getAttributeCount();
176 Map<String, String> attributes = new HashMap<String, String>( Math.max( attributeCount, 0 ) );
177 for ( int i = 0; i < attributeCount; i++ )
178 {
179 attributes.put( p.getAttributeName( i ), p.getAttributeValue( i ) );
180 }
181
182 startTag( tagPath, attributes );
183 }
184 break;
185
186 case XmlPullParser.TEXT:
187 if ( !p.isWhitespace() )
188 {
189 String text = p.getText();
190 text( tagPath, text );
191 }
192 break;
193
194 case XmlPullParser.END_TAG:
195 tagName = p.getName();
196
197 if ( lastIndex < 0 || !tagName.equals( tagPath.get( lastIndex ) ) )
198 {
199 logger.warn( "Bad tag path: " + Arrays.toString( tagPath.toArray() ) );
200 }
201 endTag( tagPath );
202 tagPath.remove( lastIndex );
203 break;
204
205 default:
206 logger.warn( "Unexpected event type " + eventType );
207 break;
208 }
209 p.next();
210 eventType = p.getEventType();
211 if ( logger.isDebugEnabled() )
212 {
213 logger.debug( "Event " + eventType );
214 }
215 }
216 }
217
218
219
220
221 public void waitComplete()
222 {
223 Thread.yield();
224 try
225 {
226 writer.close();
227 }
228 catch ( IOException e1 )
229 {
230 logger.warn( "Exception flushing output", e1 );
231 }
232
233 while ( !isComplete() )
234 {
235 synchronized ( this )
236 {
237 try
238 {
239 if ( !isComplete() )
240 {
241 this.wait( 1000 );
242 }
243 }
244 catch ( Exception e )
245 {
246 logger.warn( e );
247 }
248 }
249 }
250
251 }
252
253 private boolean isComplete()
254 {
255 return complete || lineCount == 0;
256 }
257
258 protected void startTag( List<String> tagPath, Map<String, String> attributes )
259 {
260 if ( logger.isDebugEnabled() )
261 {
262 String tagName = getTagName( tagPath );
263 logger.debug( "START_TAG: " + tagName + "(" + attributes.size() + ")" );
264 }
265 }
266
267 protected static String getTagName( List<String> tagPath )
268 {
269 return tagPath.size() == 0 ? null : tagPath.get( tagPath.size() - 1 );
270 }
271
272 protected void endTag( List<String> tagPath )
273 {
274 if ( logger.isDebugEnabled() )
275 {
276 logger.debug( "END_TAG: " + getTagName( tagPath ) );
277 }
278 }
279
280 protected void text( List<String> tagPath, String text )
281 {
282 if ( logger.isDebugEnabled() )
283 {
284 logger.debug( "TEXT: " + text );
285 }
286 }
287
288 }