1 package org.apache.maven.doxia.xsd;
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.StringReader;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29
30 import junit.framework.AssertionFailedError;
31
32 import org.apache.maven.doxia.parser.Parser;
33
34 import org.codehaus.plexus.DefaultPlexusContainer;
35 import org.codehaus.plexus.PlexusTestCase;
36 import org.codehaus.plexus.logging.Logger;
37
38 import org.xml.sax.EntityResolver;
39 import org.xml.sax.InputSource;
40 import org.xml.sax.SAXException;
41 import org.xml.sax.SAXNotRecognizedException;
42 import org.xml.sax.SAXNotSupportedException;
43 import org.xml.sax.SAXParseException;
44 import org.xml.sax.XMLReader;
45 import org.xml.sax.helpers.DefaultHandler;
46 import org.xml.sax.helpers.XMLReaderFactory;
47
48
49
50
51
52
53
54
55 public abstract class AbstractXmlValidator
56 extends PlexusTestCase
57 {
58 protected static final String EOL = System.getProperty( "line.separator" );
59
60
61 private XMLReader xmlReader;
62
63
64
65
66
67
68
69
70 protected boolean isFailErrorMessage( String message )
71 {
72 return !( message.contains( "schema_reference.4: Failed to read schema document 'http://www.w3.org/2001/xml.xsd'" )
73 || message.contains( "cvc-complex-type.4: Attribute 'alt' must appear on element 'img'." )
74 || message.contains( "cvc-complex-type.2.4.a: Invalid content starting with element" )
75 || message.contains( "cvc-complex-type.2.4.a: Invalid content was found starting with element" )
76 || message.contains( "cvc-datatype-valid.1.2.1:" )
77 || message.contains( "cvc-attribute.3:" ) );
78 }
79
80 @Override
81 protected void tearDown()
82 throws Exception
83 {
84 super.tearDown();
85
86 xmlReader = null;
87 }
88
89
90
91
92
93
94
95
96
97 public void testValidateFiles()
98 throws Exception
99 {
100 final Logger logger =
101 ( (DefaultPlexusContainer) getContainer() ).getLoggerManager().getLoggerForComponent( Parser.ROLE );
102
103 for ( Iterator<Map.Entry<String, String>> it = getTestDocuments().entrySet().iterator(); it.hasNext(); )
104 {
105 Map.Entry<String, String> entry = it.next();
106
107 if ( logger.isDebugEnabled() )
108 {
109 logger.debug( "Validate '" + entry.getKey() + "'" );
110 }
111
112 List<ErrorMessage> errors = parseXML( entry.getValue().toString() );
113
114 for ( Iterator<ErrorMessage> it2 = errors.iterator(); it2.hasNext(); )
115 {
116 ErrorMessage error = it2.next();
117
118 if ( isFailErrorMessage( error.getMessage() ) )
119 {
120 fail( entry.getKey() + EOL + error.toString() );
121 }
122 else
123 {
124 if ( logger.isDebugEnabled() )
125 {
126 logger.debug( entry.getKey() + EOL + error.toString() );
127 }
128 }
129 }
130 }
131 }
132
133
134
135
136
137 protected abstract String addNamespaces( String content );
138
139
140
141
142
143 protected abstract Map<String,String> getTestDocuments()
144 throws IOException;
145
146
147
148
149
150
151 protected abstract EntityResolver getEntityResolver();
152
153
154
155
156
157 private XMLReader getXMLReader()
158 {
159 if ( xmlReader == null )
160 {
161 try
162 {
163 xmlReader = XMLReaderFactory.createXMLReader( "org.apache.xerces.parsers.SAXParser" );
164 xmlReader.setFeature( "http://xml.org/sax/features/validation", true );
165 xmlReader.setFeature( "http://apache.org/xml/features/validation/schema", true );
166 xmlReader.setErrorHandler( new MessagesErrorHandler() );
167 xmlReader.setEntityResolver( getEntityResolver() );
168 }
169 catch ( SAXNotRecognizedException e )
170 {
171 throw new AssertionFailedError( "SAXNotRecognizedException: " + e.getMessage() );
172 }
173 catch ( SAXNotSupportedException e )
174 {
175 throw new AssertionFailedError( "SAXNotSupportedException: " + e.getMessage() );
176 }
177 catch ( SAXException e )
178 {
179 throw new AssertionFailedError( "SAXException: " + e.getMessage() );
180 }
181 }
182
183 ( (MessagesErrorHandler) xmlReader.getErrorHandler() ).clearMessages();
184
185 return xmlReader;
186 }
187
188
189
190
191
192
193
194 private List<ErrorMessage> parseXML( String content )
195 throws IOException, SAXException
196 {
197 String xmlContent = addNamespaces( content );
198
199 MessagesErrorHandler errorHandler = (MessagesErrorHandler) getXMLReader().getErrorHandler();
200
201 getXMLReader().parse( new InputSource( new StringReader( xmlContent ) ) );
202
203 return errorHandler.getMessages();
204 }
205
206 private static class ErrorMessage
207 extends DefaultHandler
208 {
209 private final String level;
210 private final String publicID;
211 private final String systemID;
212 private final int lineNumber;
213 private final int columnNumber;
214 private final String message;
215
216 ErrorMessage( String level, String publicID, String systemID, int lineNumber, int columnNumber,
217 String message )
218 {
219 super();
220 this.level = level;
221 this.publicID = publicID;
222 this.systemID = systemID;
223 this.lineNumber = lineNumber;
224 this.columnNumber = columnNumber;
225 this.message = message;
226 }
227
228
229
230
231 protected String getLevel()
232 {
233 return level;
234 }
235
236
237
238
239 protected String getPublicID()
240 {
241 return publicID;
242 }
243
244
245
246 protected String getSystemID()
247 {
248 return systemID;
249 }
250
251
252
253 protected int getLineNumber()
254 {
255 return lineNumber;
256 }
257
258
259
260 protected int getColumnNumber()
261 {
262 return columnNumber;
263 }
264
265
266
267 protected String getMessage()
268 {
269 return message;
270 }
271
272
273 @Override
274 public String toString()
275 {
276 StringBuilder sb = new StringBuilder( 512 );
277
278 sb.append( level ).append( EOL );
279 sb.append( " Public ID: " ).append( publicID ).append( EOL );
280 sb.append( " System ID: " ).append( systemID ).append( EOL );
281 sb.append( " Line number: " ).append( lineNumber ).append( EOL );
282 sb.append( " Column number: " ).append( columnNumber ).append( EOL );
283 sb.append( " Message: " ).append( message ).append( EOL );
284
285 return sb.toString();
286 }
287
288
289 @Override
290 public int hashCode()
291 {
292 final int prime = 31;
293 int result = 1;
294 result = prime * result + columnNumber;
295 result = prime * result + ( ( level == null ) ? 0 : level.hashCode() );
296 result = prime * result + lineNumber;
297 result = prime * result + ( ( message == null ) ? 0 : message.hashCode() );
298 result = prime * result + ( ( publicID == null ) ? 0 : publicID.hashCode() );
299 result = prime * result + ( ( systemID == null ) ? 0 : systemID.hashCode() );
300 return result;
301 }
302
303
304 @Override
305 public boolean equals( Object obj )
306 {
307 if ( this == obj )
308 {
309 return true;
310 }
311 if ( obj == null )
312 {
313 return false;
314 }
315 if ( getClass() != obj.getClass() )
316 {
317 return false;
318 }
319 ErrorMessage other = (ErrorMessage) obj;
320 if ( columnNumber != other.getColumnNumber() )
321 {
322 return false;
323 }
324 if ( level == null )
325 {
326 if ( other.getLevel() != null )
327 {
328 return false;
329 }
330 }
331 else if ( !level.equals( other.getLevel() ) )
332 {
333 return false;
334 }
335 if ( lineNumber != other.getLineNumber() )
336 {
337 return false;
338 }
339 if ( message == null )
340 {
341 if ( other.getMessage() != null )
342 {
343 return false;
344 }
345 }
346 else if ( !message.equals( other.getMessage() ) )
347 {
348 return false;
349 }
350 if ( publicID == null )
351 {
352 if ( other.getPublicID() != null )
353 {
354 return false;
355 }
356 }
357 else if ( !publicID.equals( other.getPublicID() ) )
358 {
359 return false;
360 }
361 if ( systemID == null )
362 {
363 if ( other.getSystemID() != null )
364 {
365 return false;
366 }
367 }
368 else if ( !systemID.equals( other.getSystemID() ) )
369 {
370 return false;
371 }
372 return true;
373 }
374 }
375
376 private static class MessagesErrorHandler
377 extends DefaultHandler
378 {
379 private final List<ErrorMessage> messages;
380
381 MessagesErrorHandler()
382 {
383 messages = new ArrayList<ErrorMessage>( 8 );
384 }
385
386
387 @Override
388 public void warning( SAXParseException e )
389 throws SAXException
390 {
391 addMessage( "Warning", e );
392 }
393
394
395 @Override
396 public void error( SAXParseException e )
397 throws SAXException
398 {
399 addMessage( "Error", e );
400 }
401
402
403 @Override
404 public void fatalError( SAXParseException e )
405 throws SAXException
406 {
407 addMessage( "Fatal error", e );
408 }
409
410 private void addMessage( String pre, SAXParseException e )
411 {
412 ErrorMessage error =
413 new ErrorMessage( pre, e.getPublicId(), e.getSystemId(), e.getLineNumber(), e.getColumnNumber(),
414 e.getMessage() );
415
416 messages.add( error );
417 }
418
419 protected List<ErrorMessage> getMessages()
420 {
421 return Collections.unmodifiableList( messages );
422 }
423
424 protected void clearMessages()
425 {
426 messages.clear();
427 }
428 }
429 }