/FileExtraction/Java_unseen/dlitz_resin/modules/resin/src/com/caucho/jstl/rt/XmlTransformTag.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.jstl.rt;

import com.caucho.jsp.PageContextImpl;
import com.caucho.jstl.NameValueTag;
import com.caucho.server.webapp.WebApp;
import com.caucho.util.L10N;
import com.caucho.vfs.*;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.BodyContent;
import javax.servlet.jsp.tagext.BodyTagSupport;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.Writer;
import java.util.ArrayList;
import java.util.logging.Logger;

public class XmlTransformTag extends BodyTagSupport implements NameValueTag {
  private static final Logger log
    = Logger.getLogger(XmlTransformTag.class.getName());
  private static final L10N L = new L10N(XmlTransformTag.class);
  
  private Object _xml;
  private Object _xslt;

  private String _xmlSystemId;
  private String _xsltSystemId;
  
  private String _var;
  private String _scope;

  private Object _result;
  
  private ArrayList<String> _paramNames = new ArrayList<String>();
  private ArrayList<String> _paramValues = new ArrayList<String>();
  
  /**
   * Sets the JSP-EL XML value.
   */
  public void setDoc(Object xml)
  {
    _xml = xml;
  }
  
  /**
   * Sets the JSP-EL XML value.
   */
  public void setXml(Object xml)
  {
    setDoc(xml);
  }

  /**
   * Sets the JSP-EL XML value.
   */
  public void setXslt(Object xslt)
  {
    _xslt = xslt;
  }

  /**
   * Sets the JSP-EL XML system id expr.
   */
  public void setXmlSystemId(String xmlSystemId)
  {
    _xmlSystemId = xmlSystemId;
  }

  /**
   * Sets the JSP-EL XML system id expr.
   */
  public void setDocSystemId(String docSystemId)
  {
    _xmlSystemId = docSystemId;
  }

  /**
   * Sets the JSP-EL XSLT system id.
   */
  public void setXsltSystemId(String xsltSystemId)
  {
    _xsltSystemId = xsltSystemId;
  }

  /**
   * Sets the variable
   */
  public void setVar(String var)
  {
    _var = var;
  }

  /**
   * Sets the scope
   */
  public void setScope(String scope)
  {
    _scope = scope;
  }

  /**
   * Sets the result
   */
  public void setResult(Object result)
  {
    _result = result;
  }
  
  /**
   * Adds a parameter.
   */
  public void addParam(String name, String value)
  {
    _paramNames.add(name);
    _paramValues.add(value);
  }

  /**
   * Process the tag.
   */
  public int doStartTag()
    throws JspException
  {
    _paramNames.clear();
    _paramValues.clear();

    return super.doStartTag();
  }

  /**
   * Process the tag.
   */
  public int doEndTag()
    throws JspException
  {
    try {
      PageContextImpl pageContext = (PageContextImpl) this.pageContext;
      
      JspWriter out = pageContext.getOut();

      TransformerFactory factory = TransformerFactory.newInstance();

      String xsltSystemId = getCanonicalURL(pageContext, _xsltSystemId);
      
      Source source = getSource(_xslt, xsltSystemId);

      Transformer transformer = factory.newTransformer(source);
      // transformer.setOutputProperty("omit-xml-declaration", "yes");

      for (int i = 0; i < _paramNames.size(); i++) {
        String name = _paramNames.get(i);
        String value = _paramValues.get(i);

        transformer.setParameter(name, value);
      }

      if (_xml != null)
        source = getSource(_xml, getCanonicalURL(pageContext, _xmlSystemId));
      else {
        BodyContent body = getBodyContent();
        
        TempCharReader tempReader = (TempCharReader) body.getReader();
        int ch;

        while (Character.isWhitespace((ch = tempReader.read()))) {
        }

        if (ch >= 0)
          tempReader.unread();
        
        source = new StreamSource(tempReader);

        if (_xmlSystemId != null)
          source.setSystemId(getCanonicalURL(pageContext, _xmlSystemId));
        else
          source.setSystemId(((HttpServletRequest) pageContext.getRequest()).getRequestURI());
      }

      Result result;
      Node top = null;

      if (_result != null) {
        result = (Result) _result;
      }
      else if (_var != null) {
        top = new com.caucho.xml.QDocument();
        
        result = new DOMResult(top);
      }
      else {
        result = new StreamResult(new WriterWrapper(out));
      }

      transformer.transform(source, result);

      if (_var != null)
        CoreSetTag.setValue(pageContext, _var, _scope, top);
    } catch (Exception e) {
      throw new JspException(e);
    }

    return SKIP_BODY;
  }

  /**
   * Returns the ML source.
   */
  private Source getSource(Object xml, String systemId)
    throws JspException
  {
    PageContextImpl pageContext = (PageContextImpl) this.pageContext;
      
    Object xmlObj = xml;
    Source source = null;

    if (xmlObj instanceof String) {
      ReadStream is = Vfs.openString((String) xmlObj);

      source = new StreamSource(is, systemId);
    }
    else if (xmlObj instanceof InputStream) {
      source = new StreamSource((InputStream) xmlObj, systemId);
    }
    else if (xmlObj instanceof Reader) {
      source = new StreamSource((Reader) xmlObj, systemId);
    }
    else if (xmlObj instanceof Node) {
      source = new DOMSource((Node) xmlObj, systemId);
    }
    else if (xmlObj instanceof NodeList) {
      source = new DOMSource(((NodeList) xmlObj).item(0), systemId);
    }
    else if (xmlObj instanceof Source)
      source = (Source) xmlObj;
    else
      throw new JspException(L.l("unknown xml object type `{0}'", xmlObj));
    
    return source;
  }

  private String getCanonicalURL(PageContextImpl pageContext, String url)
  {
    WebApp app = pageContext.getApplication();
    Path appDir = pageContext.getApplication().getRootDirectory();
      
    if (url != null) {
      if (url.startsWith("/"))
        url = app.getRealPath(url);
      else if (url.indexOf(':') > 0 &&
               url.indexOf(':') < url.indexOf('/')) {
      }
      else {
        url = pageContext.getRequest().getRealPath(url);
      }

      url = appDir.lookup(url).getURL();
    }

    return url;
  }

  static class WriterWrapper extends Writer {
    private JspWriter _out;

    WriterWrapper(JspWriter out)
    {
      _out = out;
    }
    
    public void write(char []buf, int off, int len)
      throws IOException
    {
      _out.write(buf, off, len);
    }
    
    public void write(int ch)
      throws IOException
    {
      _out.write(ch);
    }
    
    public void write(String s)
      throws IOException
    {
      _out.write(s);
    }
    
    public void write(String s, int off, int len)
      throws IOException
    {
      _out.write(s, off, len);
    }

    public void flush()
    {
    }

    public void close()
    {
    }
  }
}
