//
// FlowStepXmlTransform.java - Transform DOM Document to DOM Document
//
// We observe that although <xsl:output indent="yes"> might work using a
// SAXSource to SAXResult based XSLT operation, it doesn't seem to affect
// a DOMSource to DOMResult based one (which is what this FlowStep uses).
// As a result, even if your XSLT file has this, you'll probably want to also
// use <outputproperty name="indent" value="yes"> when converting from
// Document to String using <vartype>.
//

package nyangau.jmsadapter;

//...simports:0:
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.io.IOException;
import javax.jms.JMSException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.stream.StreamResult;
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 FlowStepXmlTransform extends FlowStep
  {
  protected String var;
  protected String destvar;
  protected String xslt; // location of XSLT file
  protected Document dTransform;
  protected Map<String,String> params;
//...sFlowStepXmlTransform:2:
public FlowStepXmlTransform(Node eStep)
  throws FlowProcessorException
  {
  super(eStep);
  NamedNodeMap nnmStep = eStep.getAttributes();
  Node aVar = nnmStep.getNamedItem("var");
  var = ( aVar != null ) ? aVar.getNodeValue() : "v0";
  Node aDestVar = nnmStep.getNamedItem("destvar");
  destvar = ( aDestVar != null ) ? aDestVar.getNodeValue() : var;
  Node aXslt = nnmStep.getNamedItem("xslt");
  if ( aXslt == null )
    throw new FlowProcessorException("unexpected missing xslt in step "+name+", bad jmsadapter.xsd?");
  xslt = aXslt.getNodeValue();

  // These are passed in to the Transformer
  params = new HashMap<String,String>();
  NodeList nlParams = eStep.getChildNodes();
  for ( int i = 0; i < nlParams.getLength(); i++ )
    {
    Node eParam = nlParams.item(i);
    if ( eParam.getNodeType() == Node.ELEMENT_NODE &&
         eParam.getNodeName().equals("param") )
      {
      NamedNodeMap nnmParam = eParam.getAttributes();
      Node aParamName = nnmParam.getNamedItem("name");
      if ( aParamName == null )
        throw new FlowProcessorException("unexpected missing param name in step "+name+", bad jmsadapter.xsd?");
      String paramname = aParamName.getNodeValue();
      Node aParamVar = nnmParam.getNamedItem("var");
      if ( aParamVar == null )
        throw new FlowProcessorException("unexpected missing param var in step "+name+", bad jmsadapter.xsd?");
      String paramvar = aParamVar.getNodeValue();
      params.put(paramname, paramvar);
      }
    }

  // DocumentBuilder is not thread safe.
  // dTransform Document should be safe to use by multiple threads at once.
  // TransformerFactory is not thread safe.
  // Transformer is not thread safe.
  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();
    dTransform = db.parse(xslt);
    }
  catch ( ParserConfigurationException e )
    {
    // Serious problems
    throw new FlowProcessorException("can't parse XSLT "+xslt, e);
    }
  catch ( SAXParseException e )
    {
    throw new FlowProcessorException("can't parse XSLT "+xslt+", line "+e.getLineNumber()+", uri "+e.getSystemId(), e);
    }
  catch ( SAXException e )
    {
    throw new FlowProcessorException("can't parse XSLT "+xslt, e);
    }
  catch ( IOException e )
    {
    throw new FlowProcessorException("can't parse XSLT "+xslt, 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;
  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
    {
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer t = tf.newTransformer(new DOMSource(dTransform));
    for ( String n : params.keySet() )
      {
      String pvar = params.get(n);
      Object po = c.getVar(pvar);
      if ( po == null )
        throw new FlowProcessorException(pvar+" undefined in step "+name);
      if ( po instanceof FlowNullValue )
        throw new FlowProcessorException(pvar+" is null in step "+name);
      if ( ! (po instanceof String) )
        throw new FlowProcessorException(pvar+" is not a String in step "+name);
      t.setParameter(n, (String) po);
      }
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document dNew = db.newDocument();
    t.transform(
      new DOMSource((Node) d),
      new DOMResult((Node) dNew)
      );
    o = dNew;
    }
  catch ( ParserConfigurationException e )
    {
    // Serious problems
    // This is raised by dbf.newDocumentBuilder()
    throw new FlowProcessorException("can't transform XML", e);
    }
  catch ( TransformerConfigurationException e )
    {
    // Serious problems
    throw new FlowProcessorException("can't transform XML", e);
    }
  catch ( TransformerException e )
    {
    throw new FlowProcessorException("can't transform XML", e);
    }
  c.setVar(destvar, o);
  }
//...e
  }
