001 package com.thaiopensource.validate.auto;
002
003 import com.thaiopensource.util.PropertyMap;
004 import com.thaiopensource.util.PropertyMapBuilder;
005 import com.thaiopensource.validate.IncorrectSchemaException;
006 import com.thaiopensource.validate.Schema;
007 import com.thaiopensource.validate.SchemaReader;
008 import com.thaiopensource.validate.ValidateProperty;
009 import com.thaiopensource.validate.Option;
010 import com.thaiopensource.xml.sax.XMLReaderCreator;
011
012 import org.xml.sax.EntityResolver;
013 import org.xml.sax.ErrorHandler;
014 import org.xml.sax.InputSource;
015 import org.xml.sax.SAXException;
016 import org.xml.sax.XMLReader;
017
018 import java.io.IOException;
019 import java.io.InputStream;
020 import java.net.URL;
021
022 public class AutoSchemaReader implements SchemaReader {
023 private final SchemaReceiverFactory srf;
024
025 public AutoSchemaReader() {
026 this(new SchemaReceiverLoader());
027 }
028
029 public AutoSchemaReader(SchemaReceiverFactory srf) {
030 this.srf = srf == null ? new SchemaReceiverLoader() : srf;
031 }
032
033 public Schema createSchema(InputSource in, PropertyMap properties)
034 throws IOException, SAXException, IncorrectSchemaException {
035 if (SchemaReceiverFactory.PROPERTY.get(properties) != srf) {
036 PropertyMapBuilder builder = new PropertyMapBuilder(properties);
037 SchemaReceiverFactory.PROPERTY.put(builder, srf);
038 properties = builder.toPropertyMap();
039 }
040 // get the entity resolver.
041 EntityResolver er = ValidateProperty.ENTITY_RESOLVER.get(properties);
042 // if we have an entity resolver and the input source is not in memory then
043 // try to resolve it through the provided entity resolver.
044 if (er != null && in.getByteStream()==null && in.getCharacterStream()==null) {
045 InputSource resolved = er.resolveEntity(in.getPublicId(), in.getSystemId());
046 if (resolved != null) {
047 in = resolved;
048 }
049 }
050 InputSource in2 = new InputSource();
051 in2.setSystemId(in.getSystemId());
052 in2.setPublicId(in.getPublicId());
053 in2.setEncoding(in.getEncoding());
054 Rewindable rewindable;
055 if (in.getCharacterStream() != null) {
056 RewindableReader rewindableReader = new RewindableReader(in.getCharacterStream());
057 in.setCharacterStream(rewindableReader);
058 in2.setCharacterStream(rewindableReader);
059 rewindable = rewindableReader;
060 } else {
061 InputStream byteStream = in.getByteStream();
062 if (byteStream == null) {
063 String systemId = in.getSystemId();
064 if (systemId == null)
065 throw new IllegalArgumentException("null systemId and null byteStream");
066 byteStream = new URL(systemId).openStream();
067 // XXX should use encoding from MIME header
068 }
069 RewindableInputStream rewindableByteStream = new RewindableInputStream(byteStream);
070 in.setByteStream(rewindableByteStream);
071 in2.setByteStream(rewindableByteStream);
072 rewindable = rewindableByteStream;
073 }
074 SchemaReceiver sr = new AutoSchemaReceiver(properties, rewindable);
075 XMLReaderCreator xrc = ValidateProperty.XML_READER_CREATOR.get(properties);
076 XMLReader xr = xrc.createXMLReader();
077 ErrorHandler eh = ValidateProperty.ERROR_HANDLER.get(properties);
078 if (eh != null)
079 xr.setErrorHandler(eh);
080 if (er != null)
081 xr.setEntityResolver(er);
082 SchemaFuture sf = sr.installHandlers(xr);
083 try {
084 try {
085 xr.parse(in);
086 return sf.getSchema();
087 }
088 catch (ReparseException e) {
089 rewindable.rewind();
090 rewindable.willNotRewind();
091 return e.reparse(in2);
092 }
093 finally {
094 rewindable.willNotRewind();
095 }
096 }
097 catch (SAXException e) {
098 // Work around broken SAX parsers that catch and wrap runtime exceptions thrown by handlers
099 Exception nested = e.getException();
100 if (nested instanceof RuntimeException)
101 sf.unwrapException((RuntimeException)nested);
102 throw e;
103 }
104 catch (RuntimeException e) {
105 throw sf.unwrapException(e);
106 }
107 }
108
109 public Option getOption(String uri) {
110 return srf.getOption(uri);
111 }
112 }