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 }