1 package net.sf.logdistiller.xml;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import java.io.*;
18 import java.util.*;
19
20 import javax.xml.parsers.DocumentBuilder;
21 import javax.xml.parsers.DocumentBuilderFactory;
22 import javax.xml.parsers.ParserConfigurationException;
23 import org.w3c.dom.Document;
24 import org.w3c.dom.Element;
25 import org.xml.sax.InputSource;
26 import org.xml.sax.SAXException;
27
28 import net.sf.logdistiller.*;
29 import net.sf.logdistiller.util.PropertiesReplacer;
30 import org.xml.sax.ErrorHandler;
31
32
33
34
35
36
37 public class DOMConfigurator
38 {
39 private final Map<String, String> overriddenProperties;
40
41 private PropertiesReplacer replacer = null;
42
43 private boolean workInProgress = false;
44
45 private boolean old;
46
47 private Set<String> warnings = null;
48
49 private LogDistiller ld = null;
50
51 public DOMConfigurator()
52 {
53 overriddenProperties = null;
54 }
55
56 public DOMConfigurator( Map<String, String> overriddenProperties )
57 {
58 this.overriddenProperties = overriddenProperties;
59 }
60
61 synchronized public LogDistiller read( File f )
62 throws ParserConfigurationException, IOException, SAXException
63 {
64 return read( f, null );
65 }
66
67 synchronized public LogDistiller read( File f, ErrorHandler errorHandler )
68 throws ParserConfigurationException, IOException, SAXException
69 {
70 return read( new FileInputStream( f ), errorHandler );
71 }
72
73 synchronized public LogDistiller read( InputStream in )
74 throws ParserConfigurationException, IOException, SAXException
75 {
76 return read( in, null );
77 }
78
79 synchronized public LogDistiller read( InputStream in, ErrorHandler errorHandler )
80 throws ParserConfigurationException, IOException, SAXException
81 {
82 return read( new InputSource( in ), errorHandler );
83 }
84
85 synchronized public LogDistiller read( InputSource inputSource )
86 throws ParserConfigurationException, IOException, SAXException
87 {
88 return read( inputSource, null );
89 }
90
91
92
93
94
95
96 protected boolean begin()
97 {
98 if ( workInProgress )
99 {
100
101 return false;
102 }
103 workInProgress = true;
104 warnings = new LinkedHashSet<String>();
105 old = false;
106 return true;
107 }
108
109 protected void end( boolean really )
110 {
111 if ( !really )
112 {
113 return;
114 }
115 workInProgress = false;
116 if ( ld != null )
117 {
118 for ( String warning : warnings )
119 {
120 ld.addWarning( warning );
121 }
122 }
123 warnings = null;
124 ld = null;
125 }
126
127 protected void addWarning( String warning )
128 {
129 warnings.add( warning );
130 }
131
132 synchronized public LogDistiller read( InputSource inputSource, ErrorHandler errorHandler )
133 throws ParserConfigurationException, IOException, SAXException
134 {
135 boolean beginning = begin();
136 try
137 {
138 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
139 dbf.setValidating( true );
140 DocumentBuilder docBuilder = dbf.newDocumentBuilder();
141 docBuilder.setErrorHandler( ( errorHandler == null ) ? new SAXErrorHandler() : errorHandler );
142 LogDistillerEntityResolver resolver = new LogDistillerEntityResolver();
143 docBuilder.setEntityResolver( resolver );
144 Document doc = docBuilder.parse( inputSource );
145
146 if ( resolver.getCurrentDtd() == null )
147 {
148 addWarning( "no DTD used: you should use " + LogDistillerEntityResolver.LATEST_DTD_URL );
149 }
150 else if ( !LogDistillerEntityResolver.LATEST_DTD.equals( resolver.getCurrentDtd() ) )
151 {
152 addWarning( "using old DTD " + resolver.getCurrentDtd() + ": you should consider upgrading to "
153 + LogDistillerEntityResolver.LATEST_DTD + " (please read upgrade documentation)" );
154 }
155
156 ld = parse( doc.getDocumentElement() );
157 return ld;
158 }
159 finally
160 {
161 end( beginning );
162 }
163 }
164
165 synchronized public LogDistiller parse( Element element )
166 {
167 boolean beginning = begin();
168 try
169 {
170 if ( !"logdistiller".equals( element.getNodeName() ) )
171 {
172 throw new IllegalArgumentException( "document root element is '" + element.getNodeName()
173 + "' where it should be 'logdistiller': see " + LogDistillerEntityResolver.LATEST_DTD_URL );
174 }
175
176 String id = element.getAttribute( "id" );
177 Map<String, String> properties =
178 parseProperties( DOMUtils.getChildElementsByTagName( element, "property" ) );
179 replacer = new PropertiesReplacer( properties );
180 String description = replacer.replaceProperties( DOMUtils.getPCDATAByTagName( element, "description" ) );
181 LogDistiller.LogType logtype = null;
182 LogDistiller.Output output = null;
183
184 Iterator<Element> iter = DOMUtils.getChildElementsByTagName( element, "logtype" );
185 if ( iter.hasNext() )
186 {
187 logtype = parseLogType( (Element) iter.next() );
188 output = parseOutput( (Element) DOMUtils.getChildElementsByTagName( element, "output" ).next() );
189 }
190 else
191 {
192 old = true;
193 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
194 "logdistiller" );
195 String logsUrl = (String) params.get( "logs.url" );
196 output =
197 new LogDistiller.Output( "logs", logsUrl, "default", "", params, buildCompatibilityReports( params ) );
198 }
199
200 LogDistiller.Category[] categories =
201 parseCategories( DOMUtils.getChildElementsByTagName( element, "category" ) );
202 LogDistiller.Group[] groups =
203 parseGroups( DOMUtils.getChildElementsByTagName( element, "group" ), categories );
204 return new LogDistiller( id, description, logtype, output, categories, groups, old );
205 }
206 finally
207 {
208 end( beginning );
209 }
210 }
211
212 protected Map<String, String> parseProperties( Iterator<Element> iter )
213 {
214 Map<String, String> map = new HashMap<String, String>();
215 while ( iter.hasNext() )
216 {
217 Element param = iter.next();
218 String name = param.getAttribute( "name" );
219 String value = param.getAttribute( "value" );
220 if ( map.put( name, value ) != null )
221 {
222 addWarning( "multiple values were defined for '" + name
223 + "' property: the last one will override other values." );
224 }
225 }
226 if ( overriddenProperties != null )
227 {
228
229 for ( Map.Entry<String, String> entry : overriddenProperties.entrySet() )
230 {
231 String name = entry.getKey();
232 if ( map.put( name, entry.getValue() ) == null )
233 {
234 throw new IllegalArgumentException( "unable to override property '" + name + "': unknown property" );
235 }
236 }
237 }
238 return map;
239 }
240
241 protected LogDistiller.LogType parseLogType( Element element )
242 {
243 String id = element.getAttribute( "id" );
244 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
245 "logdistiller/logtype" );
246 String paramAttributes = (String)params.get( "attributes" );
247
248 Attributes attributes = null;
249 Iterator<Element> iter = DOMUtils.getChildElementsByTagName( element, "attributes" );
250 if ( iter.hasNext() )
251 {
252 attributes = parseAttributes( paramAttributes, (Element) iter.next() );
253 }
254 else
255 {
256 attributes = new Attributes( paramAttributes, Attributes.NO_EXTENSIONS );
257 addWarning( "no logtype/attributes element defined: please consider adding this element (new in LogDistiller 1.1)." );
258 }
259 return new LogDistiller.LogType( id, params, attributes );
260 }
261
262 protected Attributes parseAttributes( String paramAttributes, Element element )
263 {
264 String provided = DOMUtils.getPCDATAByTagName( element, "provided" );
265 if ( provided == null )
266 {
267 addWarning( "logtype 'attributes' param has been replaced by <provided> element: please consider using this element (new in LogDistiller 1.1)" );
268 provided = paramAttributes;
269 }
270 Attributes.Extension[] extensions = parseExtensions( DOMUtils.getChildElementsByTagName( element, "extension" ) );
271 return new Attributes( provided, extensions );
272 }
273
274 protected Attributes.Extension[] parseExtensions( Iterator<Element> iter )
275 {
276 List<Attributes.Extension> extensions = new ArrayList<Attributes.Extension>();
277 while ( iter.hasNext() )
278 {
279 Element extension = iter.next();
280 extensions.add( parseExtension( extension ) );
281 }
282 return extensions.toArray( new Attributes.Extension[extensions.size()] );
283 }
284
285 protected Attributes.Extension parseExtension( Element element )
286 {
287 String source = element.getAttribute( "source" );
288 String provides = element.getAttribute( "provides" );
289 String regexp = DOMUtils.getPCDATA( element );
290 return new Attributes.Extension( source, provides, regexp );
291 }
292
293 protected LogDistiller.Output parseOutput( Element element )
294 {
295 String directory = replacer.replaceProperties( element.getAttribute( "directory" ) );
296 String url = replacer.replaceProperties( element.getAttribute( "url" ) );
297 String content = replacer.replaceProperties( element.getAttribute( "content" ) );
298 String skip = replacer.replaceProperties( element.getAttribute( "skip" ) );
299 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
300 "logdistiller/output" );
301
302 String paramLogsUrl = params.get( "logs.url" );
303 if ( paramLogsUrl != null )
304 {
305 if ( url == null )
306 {
307 url = paramLogsUrl;
308 addWarning( "in output element, param logs.url is now deprecated: please use url attribute (<output url=\"...\">)" );
309 }
310 else
311 {
312 addWarning( "in output element, attribute url and deprecated param logs.url have both been set: ignoring param" );
313 }
314 }
315
316 LogDistiller.Report[] reports = parseReports( DOMUtils.getChildElementsByTagName( element, "report" ),
317 "logdistiller/output" );
318
319 return new LogDistiller.Output( directory, url, content, skip, params, reports );
320 }
321
322 protected LogDistiller.Report[] parseReports( Iterator<Element> iter, String context )
323 {
324 List<LogDistiller.Report> reports = new ArrayList<LogDistiller.Report>();
325 while ( iter.hasNext() )
326 {
327 Element report = iter.next();
328 reports.add( parseReport( report, context ) );
329 }
330 return reports.toArray( new LogDistiller.Report[reports.size()] );
331 }
332
333 protected LogDistiller.Report parseReport( Element element, String context )
334 {
335 String publisher = replacer.replaceProperties( element.getAttribute( "publisher" ) );
336 String format = replacer.replaceProperties( element.getAttribute( "format" ) );
337 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
338 context + "/report[publisher='" + publisher + ']' );
339 return new LogDistiller.Report( publisher, format, params );
340 }
341
342 protected LogDistiller.Report[] buildCompatibilityReports( Map<String, String> params )
343 {
344 LogDistiller.Report fileReport = new LogDistiller.Report( "file", "txt", new HashMap<String, String>() );
345 String to = (String) params.get( "mail.to" );
346 if ( to == null )
347 {
348 return new LogDistiller.Report[] { fileReport };
349 }
350 Map<String, String> newParams = new HashMap<String, String>();
351 newParams.put( "to", replacer.replaceProperties( to ) );
352 String cc = params.get( "mail.cc" );
353 if ( cc != null )
354 {
355 newParams.put( "cc", replacer.replaceProperties( cc ) );
356 }
357 LogDistiller.Report mailReport = new LogDistiller.Report( "mail", "txt", newParams );
358 return new LogDistiller.Report[] { fileReport, mailReport };
359 }
360
361 protected Map<String, String> parseParams( Iterator<Element> iter, String context )
362 {
363 Map<String, String> params = new HashMap<String, String>();
364 while ( iter.hasNext() )
365 {
366 Element param = iter.next();
367 String name = param.getAttribute( "name" );
368 String value = replacer.replaceProperties( DOMUtils.getPCDATA( param ) );
369 if ( params.put( name, value ) != null )
370 {
371 addWarning( "(" + context + ") multiple values were defined for '" + name
372 + "' parameter: the last one will override other values." );
373 }
374 }
375 return params;
376 }
377
378 protected LogDistiller.Category[] parseCategories( Iterator<Element> iter )
379 {
380 List<LogDistiller.Category> categories = new ArrayList<LogDistiller.Category>();
381 while ( iter.hasNext() )
382 {
383 Element category = iter.next();
384 String id = category.getAttribute( "id" );
385 String description = DOMUtils.getPCDATA( category );
386 categories.add( new LogDistiller.Category( id, description ) );
387 }
388 return categories.toArray( new LogDistiller.Category[categories.size()] );
389 }
390
391 protected LogDistiller.Group[] parseGroups( Iterator<Element> iter, LogDistiller.Category[] categories )
392 {
393 Map<String, LogDistiller.Category> mapCategories = new HashMap<String, LogDistiller.Category>();
394 for ( int i = 0; i < categories.length; i++ )
395 {
396 LogDistiller.Category category = categories[i];
397 mapCategories.put( category.getId(), category );
398 }
399 List<LogDistiller.Group> groups = new ArrayList<LogDistiller.Group>();
400 while ( iter.hasNext() )
401 {
402 Element group = iter.next();
403 groups.add( parseGroup( group, mapCategories ) );
404 }
405 return groups.toArray( new LogDistiller.Group[groups.size()] );
406 }
407
408 protected LogDistiller.Group parseGroup( Element element, Map<String, LogDistiller.Category> categories )
409 {
410 String id = element.getAttribute( "id" );
411 String description = replacer.replaceProperties( DOMUtils.getPCDATAByTagName( element, "description" ) );
412 String attr = replacer.replaceProperties( element.getAttribute( "continueProcessing" ) );
413 boolean continueProcessing = Boolean.valueOf( ( attr == null ) ? "false" : attr ).booleanValue();
414 attr = element.getAttribute( "save" );
415 if ( attr == null )
416 {
417 attr = "true";
418 }
419 boolean save = !"false".equals( attr );
420 String context = "logdistiller/group[id='" + id + "']";
421 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
422 context );
423 Condition[] conditions = parseConditions( DOMUtils.getChildElementsByTagName( element, "condition" ) );
424 LogDistiller.Report[] reports = parseReports( DOMUtils.getChildElementsByTagName( element, "report" ),
425 context );
426 LogDistiller.Plugin[] plugins = parsePlugins( DOMUtils.getChildElementsByTagName( element, "plugin" ),
427 context );
428 if ( old )
429 {
430 reports = buildCompatibilityReports( params );
431 }
432 LogDistiller.Category category = (LogDistiller.Category) categories.get( element.getAttribute( "category" ) );
433 return new LogDistiller.Group( id, description, continueProcessing, save, params, conditions, reports, plugins,
434 category );
435 }
436
437 protected Condition[] parseConditions( Iterator<Element> iter )
438 {
439 List<Condition> conditions = new ArrayList<Condition>();
440 while ( iter.hasNext() )
441 {
442 Element condition = iter.next();
443 conditions.add( parseCondition( condition ) );
444 }
445 return conditions.toArray( new Condition[conditions.size()] );
446 }
447
448 protected Condition parseCondition( Element element )
449 {
450 String tags = element.getAttribute( "tags" );
451 Match[] matches = parseMatchs( DOMUtils.getChildElementsByTagName( element, "match" ) );
452 return new Condition( tags, matches );
453 }
454
455 protected Match[] parseMatchs( Iterator<Element> iter )
456 {
457 List<Match> matchs = new ArrayList<Match>();
458 while ( iter.hasNext() )
459 {
460 Element match = iter.next();
461 matchs.add( parseMatch( match ) );
462 }
463 return matchs.toArray( new Match[matchs.size()] );
464 }
465
466 protected Match parseMatch( Element element )
467 {
468 String attribute = element.getAttribute( "attribute" );
469 String type = element.getAttribute( "type" );
470 String reference = replacer.replaceProperties( DOMUtils.getPCDATA( element ) );
471 return new Match( attribute, type, reference );
472 }
473
474 protected LogDistiller.Plugin[] parsePlugins( Iterator<Element> iter, String context )
475 {
476 List<LogDistiller.Plugin> plugins = new ArrayList<LogDistiller.Plugin>();
477 while ( iter.hasNext() )
478 {
479 Element plugin = iter.next();
480 plugins.add( parsePlugin( plugin, context ) );
481 }
482 return plugins.toArray( new LogDistiller.Plugin[plugins.size()] );
483 }
484
485 protected LogDistiller.Plugin parsePlugin( Element element, String context )
486 {
487 String type = element.getAttribute( "type" );
488 if ( Plugins.getPlugin( type ) == null )
489 {
490 throw new PluginConfigException( "(" + context + ") plugin type '" + type + "' unknown, valid values: "
491 + Plugins.listAllPluginIds() );
492 }
493 String attr = element.getAttribute( "globalReport" );
494 boolean globalReport = Boolean.valueOf( ( attr == null ) ? "true" : attr ).booleanValue();
495 attr = element.getAttribute( "groupReport" );
496 boolean groupReport = Boolean.valueOf( ( attr == null ) ? "true" : attr ).booleanValue();
497 Map<String, String> params = parseParams( DOMUtils.getChildElementsByTagName( element, "param" ),
498 context + "/plugin[type='" + type + "']" );
499 return new LogDistiller.Plugin( type, globalReport, groupReport, params );
500 }
501 }