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