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    }