//
// FlowStepXmlValidate.java - Validate DOM Document
//
// Note we don't support validation against DTDs.
// This would have to be done at document parsing time.
// We support XSD schemas, and maybe RELAX-NG, if you've got it.
//

package nyangau.jmsadapter;

//...simports:0:
import java.util.Vector;
import java.io.IOException;
import java.io.StringReader;
import java.net.URL;
import javax.jms.JMSException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import org.xml.sax.InputSource;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
//...e

public class FlowStepXmlValidate extends FlowStep
  {
  protected String var;
  protected String schematype;
  protected String schema; // or null
  protected Schema s;
//...sFlowStepXmlValidate:2:
public FlowStepXmlValidate(Node eStep)
  throws FlowProcessorException
  {
  super(eStep);
  NamedNodeMap nnmStep = eStep.getAttributes();
  Node aVar = nnmStep.getNamedItem("var");
  var = ( aVar != null ) ? aVar.getNodeValue() : "v0";
  Node aSchemaType = nnmStep.getNamedItem("schematype");
  schematype = ( aSchemaType != null ) ? aSchemaType.getNodeValue() : XMLConstants.W3C_XML_SCHEMA_NS_URI;
  Node aSchema = nnmStep.getNamedItem("schema");
  schema = ( aSchema != null ) ? aSchema.getNodeValue() : null;

  Document dSchema = null;
  if ( schema != null && schematype.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI) )
    {
    // DocumentBuilder is not thread safe.
    // dSchema Document should be safe to use by multiple threads at once.
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    // not validating
    // namespace aware
    // does not ignore "ignoreable" white space
    // expands entity references
    // does not ignore comments
    // does not coalesce CDATA
    dbf.setNamespaceAware(true);
    try
      {
      DocumentBuilder db = dbf.newDocumentBuilder();
      dSchema = db.parse(schema);
      }
    catch ( ParserConfigurationException e )
      {
      // Serious problems
      throw new FlowProcessorException("can't parse schema "+schema, e);
      }
    catch ( SAXParseException e )
      {
      throw new FlowProcessorException("can't parse schema "+schema+", line "+e.getLineNumber()+", uri "+e.getSystemId(), e);
      }
    catch ( SAXException e )
      {
      throw new FlowProcessorException("can't parse schema "+schema, e);
      }
    catch ( IOException e )
      {
      throw new FlowProcessorException("can't parse schema "+schema, e);
      }
    }

  try
    {
    SchemaFactory sf = SchemaFactory.newInstance(schematype);
    if ( schema == null )
      s = sf.newSchema(); // "special" schema, meaning use refs in XML
    else if ( schematype.equals(XMLConstants.W3C_XML_SCHEMA_NS_URI) )
      s = sf.newSchema(new DOMSource(dSchema));
    else
      s = sf.newSchema(new URL(schema));
    }
  catch ( SAXException e )
    {
    throw new FlowProcessorException("SAXException in newSchema in step "+name, e);
    }
  catch ( IOException e )
    {
    // This won't happen when we're using XSD schemas,
    // but it could happen with other schema types.
    throw new FlowProcessorException("IOException in newSchema in step "+name, e);
    }
  }
//...e
//...sprocess:2:
public void process(FlowContext c)
  throws FlowProcessorException, JMSException
  {
  Object o = c.getVar(var);
  if ( o == null )
    throw new FlowProcessorException(var+" undefined in step "+name);
  if ( o instanceof FlowNullValue )
    throw new FlowProcessorException(var+" is null in step "+name);
  if ( ! (o instanceof Document) )
    throw new FlowProcessorException(var+" is not a Document in step "+name);

  Document d = (Document) o;
  try
    {
    Validator v = s.newValidator();
    v.validate(new DOMSource(d));
    }
  catch ( SAXException e )
    {
    throw new FlowProcessorException("XML validation failed in step "+name, e);
    }
  catch ( IOException e )
    {
    throw new FlowProcessorException("unexpected IOException, sources are in memory", e);
    }
  }
//...e
  }
