~ubuntu-branches/ubuntu/trusty/jing-trang/trusty-proposed

« back to all changes in this revision

Viewing changes to mod/schematron/src/main/com/thaiopensource/validate/schematron/ISOSchemaReaderImpl.java

  • Committer: Package Import Robot
  • Author(s): Samuel Thibault, Samuel Thibault, Eugene Zhukov
  • Date: 2013-12-15 21:28:29 UTC
  • mfrom: (6.1.3 sid)
  • Revision ID: package-import@ubuntu.com-20131215212829-j6ffnwbhdrt27vay
Tags: 20131210+dfsg-1
[ Samuel Thibault ]
* rules: Add build-indep and build-arch rules.
* Bump Standards-Version to 3.9.5.
* watch: Add dfsg mangling.
* debian/source: switch to 3.0 format.

[ Eugene Zhukov ]
* New upstream release (Closes: Bug#732003).
* use canonical URLs for the Vcs-* fields in d/control.
* rules, control: Use libsaxonhe-java instead of libsaxonb-java.
* rules: maven artifacts to local maven repo.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package com.thaiopensource.validate.schematron;
 
2
 
 
3
import com.thaiopensource.util.Localizer;
 
4
import com.thaiopensource.util.PropertyId;
 
5
import com.thaiopensource.util.PropertyMap;
 
6
import com.thaiopensource.util.PropertyMapBuilder;
 
7
import com.thaiopensource.validate.AbstractSchemaReader;
 
8
import com.thaiopensource.validate.IncorrectSchemaException;
 
9
import com.thaiopensource.validate.Option;
 
10
import com.thaiopensource.validate.ResolverFactory;
 
11
import com.thaiopensource.validate.Schema;
 
12
import com.thaiopensource.validate.ValidateProperty;
 
13
import com.thaiopensource.validate.Validator;
 
14
import com.thaiopensource.validate.prop.rng.RngProperty;
 
15
import com.thaiopensource.validate.prop.schematron.SchematronProperty;
 
16
import com.thaiopensource.validate.rng.CompactSchemaReader;
 
17
import com.thaiopensource.xml.sax.CountingErrorHandler;
 
18
import com.thaiopensource.xml.sax.DelegatingContentHandler;
 
19
import com.thaiopensource.xml.sax.DraconianErrorHandler;
 
20
import org.xml.sax.Attributes;
 
21
import org.xml.sax.ContentHandler;
 
22
import org.xml.sax.ErrorHandler;
 
23
import org.xml.sax.InputSource;
 
24
import org.xml.sax.Locator;
 
25
import org.xml.sax.SAXException;
 
26
import org.xml.sax.SAXParseException;
 
27
import org.xml.sax.XMLReader;
 
28
 
 
29
import javax.xml.transform.ErrorListener;
 
30
import javax.xml.transform.SourceLocator;
 
31
import javax.xml.transform.Templates;
 
32
import javax.xml.transform.Transformer;
 
33
import javax.xml.transform.TransformerConfigurationException;
 
34
import javax.xml.transform.TransformerException;
 
35
import javax.xml.transform.TransformerFactory;
 
36
import javax.xml.transform.sax.SAXResult;
 
37
import javax.xml.transform.sax.SAXSource;
 
38
import javax.xml.transform.sax.SAXTransformerFactory;
 
39
import javax.xml.transform.sax.TemplatesHandler;
 
40
import javax.xml.transform.sax.TransformerHandler;
 
41
import javax.xml.transform.stream.StreamSource;
 
42
import java.io.IOException;
 
43
import java.io.InputStream;
 
44
 
 
45
class ISOSchemaReaderImpl extends AbstractSchemaReader {
 
46
  static final String SCHEMATRON_URI = "http://purl.oclc.org/dsdl/schematron";
 
47
  private static final String LOCATION_URI = "http://www.thaiopensource.com/ns/location";
 
48
  private static final String ERROR_URI = "http://www.thaiopensource.com/ns/error";
 
49
  private final Localizer localizer = new Localizer(ISOSchemaReaderImpl.class);
 
50
 
 
51
  private final Class<? extends SAXTransformerFactory> transformerFactoryClass;
 
52
  private final TransformerFactoryInitializer transformerFactoryInitializer;
 
53
  private final Templates schematron;
 
54
  private final Schema schematronSchema;
 
55
  private static final String SCHEMATRON_SCHEMA = "iso-schematron.rnc";
 
56
  private static final String SCHEMATRON_STYLESHEET = "iso-schematron.xsl";
 
57
  // XSLTC has some problems with extension functions and function-available, so
 
58
  // we need a separate stylesheet.  See
 
59
  // https://issues.apache.org/jira/browse/XALANJ-2464
 
60
  // https://issues.apache.org/jira/browse/XALANJ-2465
 
61
  private static final String SCHEMATRON_XSLTC_STYLESHEET = "iso-schematron-xsltc.xsl";
 
62
  private static final PropertyId<?>[] supportedPropertyIds = {
 
63
    ValidateProperty.ERROR_HANDLER,
 
64
    ValidateProperty.XML_READER_CREATOR,
 
65
    ValidateProperty.ENTITY_RESOLVER,
 
66
    ValidateProperty.URI_RESOLVER,
 
67
    ValidateProperty.RESOLVER,
 
68
    SchematronProperty.DIAGNOSE,
 
69
    SchematronProperty.PHASE,
 
70
  };
 
71
 
 
72
  ISOSchemaReaderImpl(SAXTransformerFactory transformerFactory, TransformerFactoryInitializer transformerFactoryInitializer)
 
73
          throws TransformerConfigurationException, IncorrectSchemaException {
 
74
    this.transformerFactoryClass = transformerFactory.getClass();
 
75
    this.transformerFactoryInitializer = transformerFactoryInitializer;
 
76
    final boolean isXsltc = isXsltc(transformerFactoryClass);
 
77
    final String stylesheet = isXsltc ? SCHEMATRON_XSLTC_STYLESHEET : SCHEMATRON_STYLESHEET;
 
78
    final String resourceName = fullResourceName(stylesheet);
 
79
    final StreamSource source = new StreamSource(getResourceAsStream(resourceName));
 
80
    initTransformerFactory(transformerFactory);
 
81
    schematron = transformerFactory.newTemplates(source);
 
82
    InputSource schemaSource = new InputSource(getResourceAsStream(fullResourceName(SCHEMATRON_SCHEMA)));
 
83
    PropertyMapBuilder builder = new PropertyMapBuilder();
 
84
    builder.put(ValidateProperty.ERROR_HANDLER, new DraconianErrorHandler());
 
85
    RngProperty.CHECK_ID_IDREF.add(builder);
 
86
    try {
 
87
      schematronSchema = CompactSchemaReader.getInstance().createSchema(schemaSource, builder.toPropertyMap());
 
88
    }
 
89
    catch (SAXException e) {
 
90
      throw new IncorrectSchemaException();
 
91
    }
 
92
    catch (IOException e) {
 
93
      throw new IncorrectSchemaException();
 
94
    }
 
95
  }
 
96
 
 
97
  static boolean isXsltc(Class<? extends SAXTransformerFactory> cls) {
 
98
    return cls.getName().indexOf(".xsltc.") >= 0;
 
99
  }
 
100
 
 
101
  public Option getOption(String uri) {
 
102
    return SchematronProperty.getOption(uri);
 
103
  }
 
104
 
 
105
  private void initTransformerFactory(TransformerFactory factory) {
 
106
    transformerFactoryInitializer.initTransformerFactory(factory);
 
107
  }
 
108
 
 
109
  static class UserException extends Exception {
 
110
    private final SAXException exception;
 
111
 
 
112
    UserException(SAXException exception) {
 
113
      this.exception = exception;
 
114
    }
 
115
 
 
116
    SAXException getException() {
 
117
      return exception;
 
118
    }
 
119
  }
 
120
 
 
121
  static class UserWrapErrorHandler extends CountingErrorHandler {
 
122
    UserWrapErrorHandler(ErrorHandler errorHandler) {
 
123
      super(errorHandler);
 
124
    }
 
125
 
 
126
    public void warning(SAXParseException exception)
 
127
            throws SAXException {
 
128
      try {
 
129
        super.warning(exception);
 
130
      }
 
131
      catch (SAXException e) {
 
132
        throw new SAXException(new UserException(e));
 
133
      }
 
134
    }
 
135
 
 
136
    public void error(SAXParseException exception)
 
137
            throws SAXException {
 
138
      try {
 
139
        super.error(exception);
 
140
      }
 
141
      catch (SAXException e) {
 
142
        throw new SAXException(new UserException(e));
 
143
      }
 
144
    }
 
145
 
 
146
    public void fatalError(SAXParseException exception)
 
147
            throws SAXException {
 
148
      try {
 
149
        super.fatalError(exception);
 
150
      }
 
151
      catch (SAXException e) {
 
152
        throw new SAXException(new UserException(e));
 
153
      }
 
154
    }
 
155
  }
 
156
 
 
157
  static class ErrorFilter extends DelegatingContentHandler {
 
158
    private final ErrorHandler eh;
 
159
    private final Localizer localizer;
 
160
    private Locator locator;
 
161
 
 
162
    ErrorFilter(ContentHandler delegate, ErrorHandler eh, Localizer localizer) {
 
163
      super(delegate);
 
164
      this.eh = eh;
 
165
      this.localizer = localizer;
 
166
    }
 
167
 
 
168
    public void setDocumentLocator(Locator locator) {
 
169
      this.locator = locator;
 
170
      super.setDocumentLocator(locator);
 
171
    }
 
172
 
 
173
    public void startElement(String namespaceURI, String localName,
 
174
                             String qName, Attributes atts)
 
175
            throws SAXException {
 
176
      if (namespaceURI.equals(ERROR_URI) && localName.equals("error"))
 
177
        eh.error(new SAXParseException(localizer.message(atts.getValue("", "message"),
 
178
                                                         atts.getValue("", "arg")),
 
179
                                       locator));
 
180
      super.startElement(namespaceURI, localName, qName, atts);
 
181
    }
 
182
  }
 
183
 
 
184
  static class LocationFilter extends DelegatingContentHandler implements Locator {
 
185
    private final String mainSystemId;
 
186
    private String systemId = null;
 
187
    private int lineNumber = -1;
 
188
    private int columnNumber = -1;
 
189
    private SAXException exception = null;
 
190
 
 
191
    LocationFilter(ContentHandler delegate, String systemId) {
 
192
      super(delegate);
 
193
      this.mainSystemId = systemId;
 
194
    }
 
195
 
 
196
    SAXException getException() {
 
197
      return exception;
 
198
    }
 
199
 
 
200
    public void setDocumentLocator(Locator locator) {
 
201
    }
 
202
 
 
203
    public void startDocument()
 
204
            throws SAXException {
 
205
      getDelegate().setDocumentLocator(this);
 
206
      super.startDocument();
 
207
    }
 
208
 
 
209
    public void startElement(String namespaceURI, String localName,
 
210
                             String qName, Attributes atts)
 
211
            throws SAXException {
 
212
      systemId = getLocationAttribute(atts, "system-id");
 
213
      lineNumber = toInteger(getLocationAttribute(atts, "line-number"));
 
214
      columnNumber = toInteger(getLocationAttribute(atts, "column-number"));
 
215
      try {
 
216
        super.startElement(namespaceURI, localName, qName, atts);
 
217
      }
 
218
      catch (SAXException e) {
 
219
        this.exception = e;
 
220
        setDelegate(null);
 
221
      }
 
222
      systemId = null;
 
223
      lineNumber = -1;
 
224
      columnNumber = -1;
 
225
    }
 
226
 
 
227
    private static String getLocationAttribute(Attributes atts, String name) {
 
228
      return atts.getValue(LOCATION_URI, name);
 
229
    }
 
230
 
 
231
    private static int toInteger(String value) {
 
232
      if (value == null)
 
233
        return -1;
 
234
      try {
 
235
        return Integer.parseInt(value);
 
236
      }
 
237
      catch (NumberFormatException e) {
 
238
        return -1;
 
239
      }
 
240
    }
 
241
 
 
242
    public String getPublicId() {
 
243
      return null;
 
244
    }
 
245
 
 
246
    public String getSystemId() {
 
247
      if (systemId != null && !systemId.equals(""))
 
248
        return systemId;
 
249
      return mainSystemId;
 
250
    }
 
251
 
 
252
    public int getLineNumber() {
 
253
      return lineNumber;
 
254
    }
 
255
 
 
256
    public int getColumnNumber() {
 
257
      return columnNumber;
 
258
    }
 
259
  }
 
260
 
 
261
  static class SAXErrorListener implements ErrorListener {
 
262
     private final ErrorHandler eh;
 
263
     private final String systemId;
 
264
     private boolean hadError = false;
 
265
     SAXErrorListener(ErrorHandler eh, String systemId) {
 
266
       this.eh = eh;
 
267
       this.systemId = systemId;
 
268
     }
 
269
 
 
270
     boolean getHadError() {
 
271
       return hadError;
 
272
     }
 
273
 
 
274
     public void warning(TransformerException exception)
 
275
             throws TransformerException {
 
276
       SAXParseException spe = transform(exception);
 
277
       try {
 
278
         eh.warning(spe);
 
279
       }
 
280
       catch (SAXException e) {
 
281
         throw new TransformerException(new UserException(e));
 
282
       }
 
283
     }
 
284
 
 
285
     public void error(TransformerException exception)
 
286
             throws TransformerException {
 
287
       hadError = true;
 
288
       SAXParseException spe = transform(exception);
 
289
       try {
 
290
         eh.error(spe);
 
291
       }
 
292
       catch (SAXException e) {
 
293
         throw new TransformerException(new UserException(e));
 
294
       }
 
295
     }
 
296
 
 
297
     public void fatalError(TransformerException exception)
 
298
             throws TransformerException {
 
299
       hadError = true;
 
300
       SAXParseException spe = transform(exception);
 
301
       try {
 
302
         eh.fatalError(spe);
 
303
       }
 
304
       catch (SAXException e) {
 
305
         throw new TransformerException(new UserException(e));
 
306
       }
 
307
     }
 
308
 
 
309
     SAXParseException transform(TransformerException exception) throws TransformerException {
 
310
       Throwable cause = exception.getException();
 
311
       // Xalan takes it upon itself to catch exceptions and pass them to the ErrorListener.
 
312
       if (cause instanceof RuntimeException)
 
313
         throw (RuntimeException)cause;
 
314
       if (cause instanceof SAXException
 
315
           || cause instanceof IncorrectSchemaException
 
316
           || cause instanceof IOException)
 
317
         throw exception;
 
318
       SourceLocator locator = exception.getLocator();
 
319
       if (locator == null)
 
320
         return new SAXParseException(exception.getMessage(), null);
 
321
       // Xalan sometimes loses the mainSystemId; work around this.
 
322
       String s = locator.getSystemId();
 
323
       if (s == null)
 
324
         s = systemId;
 
325
       return new SAXParseException(exception.getMessage(),
 
326
                                    locator.getPublicId(),
 
327
                                    s,
 
328
                                    locator.getLineNumber(),
 
329
                                    locator.getColumnNumber());
 
330
     }
 
331
   }
 
332
 
 
333
 
 
334
  // Minor problem is that
 
335
  // Saxon 6.5.2 prints to System.err in TemplatesHandlerImpl.getTemplates().
 
336
 
 
337
  public Schema createSchema(SAXSource source, PropertyMap properties)
 
338
            throws IOException, SAXException, IncorrectSchemaException {
 
339
    ErrorHandler eh = properties.get(ValidateProperty.ERROR_HANDLER);
 
340
    CountingErrorHandler ceh = new CountingErrorHandler(eh);
 
341
    InputSource in = source.getInputSource();
 
342
    String systemId = in.getSystemId();
 
343
    IfValidHandler ifValidHandler = new IfValidHandler();
 
344
    ifValidHandler.setErrorHandler(ceh);
 
345
    try {
 
346
      SAXTransformerFactory factory = (SAXTransformerFactory)transformerFactoryClass.newInstance();
 
347
      initTransformerFactory(factory);
 
348
      TransformerHandler transformerHandler = factory.newTransformerHandler(schematron);
 
349
      ifValidHandler.setDelegate(transformerHandler);
 
350
      Transformer transformer = transformerHandler.getTransformer();
 
351
      String phase = properties.get(SchematronProperty.PHASE);
 
352
      if (phase != null)
 
353
        transformer.setParameter("phase", phase);
 
354
      boolean diagnose = properties.contains(SchematronProperty.DIAGNOSE);
 
355
      if (diagnose)
 
356
        transformer.setParameter("diagnose", Boolean.TRUE);
 
357
      PropertyMapBuilder builder = new PropertyMapBuilder(properties);
 
358
      builder.put(ValidateProperty.ERROR_HANDLER, ifValidHandler);
 
359
      Validator validator = schematronSchema.createValidator(builder.toPropertyMap());
 
360
      ifValidHandler.setValidator(validator.getContentHandler());
 
361
      XMLReader xr = source.getXMLReader();
 
362
      if (xr == null)
 
363
        xr = ResolverFactory.createResolver(properties).createXMLReader();
 
364
      xr.setContentHandler(ifValidHandler);      
 
365
      xr.setDTDHandler(validator.getDTDHandler());  // not strictly necessary
 
366
      factory.setErrorListener(new SAXErrorListener(ceh, systemId));
 
367
      TemplatesHandler templatesHandler = factory.newTemplatesHandler();
 
368
      templatesHandler.setSystemId(systemId);
 
369
      LocationFilter stage2 = new LocationFilter(new ErrorFilter(templatesHandler, ceh, localizer), systemId);
 
370
      transformerHandler.setResult(new SAXResult(stage2));
 
371
      xr.setErrorHandler(ceh);
 
372
      xr.parse(in);
 
373
      SAXException exception = stage2.getException();
 
374
      if (exception != null)
 
375
        throw exception;
 
376
      if (ceh.getHadErrorOrFatalError())
 
377
        throw new IncorrectSchemaException();
 
378
      // Getting the templates can cause errors to be generated.
 
379
      Templates templates = templatesHandler.getTemplates();
 
380
      if (ceh.getHadErrorOrFatalError())
 
381
        throw new IncorrectSchemaException();
 
382
      return new SchemaImpl(templates,
 
383
                            transformerFactoryClass,
 
384
                            properties,
 
385
                            supportedPropertyIds);
 
386
    }
 
387
    catch (SAXException e) {
 
388
      throw cleanupSAXException(e);
 
389
    }
 
390
    catch (TransformerConfigurationException e) {
 
391
      throw new SAXException(localizer.message("unexpected_schema_creation_error"));
 
392
    }
 
393
    catch (InstantiationException e) {
 
394
      throw new SAXException(e);
 
395
    }
 
396
    catch (IllegalAccessException e) {
 
397
      throw new SAXException(e);
 
398
    }
 
399
  }
 
400
 
 
401
  private static String fullResourceName(String name) {
 
402
    String className = ISOSchemaReaderImpl.class.getName();
 
403
    return className.substring(0, className.lastIndexOf('.')).replace('.', '/') + "/resources/" + name;
 
404
  }
 
405
 
 
406
  private static InputStream getResourceAsStream(String resourceName) {
 
407
    ClassLoader cl = ISOSchemaReaderImpl.class.getClassLoader();
 
408
    // XXX see if we should borrow 1.2 code from Service
 
409
    if (cl == null)
 
410
      return ClassLoader.getSystemResourceAsStream(resourceName);
 
411
    else
 
412
      return cl.getResourceAsStream(resourceName);
 
413
  }
 
414
 
 
415
  private static SAXException cleanupSAXException(SAXException saxException) {
 
416
    if (exceptionHasLocation(saxException))
 
417
      return saxException;
 
418
    Exception exception = saxException.getException();
 
419
    if (exception instanceof SAXException && exception.getMessage() == null)
 
420
      return cleanupSAXException((SAXException)exception);
 
421
    if (exception instanceof TransformerException)
 
422
      return cleanupTransformerException((TransformerException)exception);
 
423
    return saxException;     
 
424
  }
 
425
 
 
426
  private static SAXException cleanupTransformerException(TransformerException e) {
 
427
    String message = e.getMessage();
 
428
    Throwable cause = e.getException();
 
429
    SourceLocator transformLoc = e.getLocator();
 
430
    // a TransformerException created with just a Throwable t argument
 
431
    // gets a message of t.toString()
 
432
    if (message != null && cause != null && message.equals(cause.toString()))
 
433
      message = null;
 
434
    if (message == null && cause instanceof SAXException && transformLoc == null)
 
435
      return cleanupSAXException((SAXException)cause);
 
436
    if (cause instanceof TransformerException && transformLoc == null)
 
437
      return cleanupTransformerException((TransformerException)cause);
 
438
    Exception exception = null;
 
439
    if (cause instanceof Exception)
 
440
      exception = (Exception)cause;
 
441
    String publicId = null;
 
442
    String systemId = null;
 
443
    int lineNumber = -1;
 
444
    int columnNumber = -1;
 
445
    if (transformLoc != null) {
 
446
      publicId = transformLoc.getPublicId();
 
447
      systemId = transformLoc.getSystemId();
 
448
      lineNumber = transformLoc.getLineNumber();
 
449
      columnNumber = transformLoc.getColumnNumber();
 
450
    }
 
451
    if (publicId != null || systemId != null || lineNumber >= 0 || columnNumber >= 0)
 
452
      return new SAXParseException(message, publicId, systemId, lineNumber, columnNumber, exception);
 
453
    return new SAXException(message, exception);
 
454
  }
 
455
 
 
456
  private static boolean exceptionHasLocation(SAXException saxException) {
 
457
    if (!(saxException instanceof SAXParseException))
 
458
      return false;
 
459
    SAXParseException pe = (SAXParseException)saxException;
 
460
    return (pe.getPublicId() != null || pe.getSystemId() != null
 
461
            || pe.getLineNumber() >= 0 || pe.getColumnNumber() >= 0);
 
462
  }
 
463
}