/*
* WebWork, Web Application Framework
*
* Distributable under Apache license.
* See terms of license at opensource.org
*/
package webwork.xslt;

import org.apache.log4j.Category;
import org.xml.sax.InputSource;
import webwork.util.ValueStack;
import webwork.xslt.SAXAdapter.XMLReaderAdapter;
import webwork.xslt.SAXAdapter.XMLWalker;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.*;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * XSL presentation servlet. Markup the result as XML and process it
 * with an XSLT Stylesheet.
 *
 * This allows you to use XSLT stylesheets as views of WebWork actions.
 *
 * @author Rickard Öberg (rickard@dreambean.com)
 * @author Philipp meier (meier@o-matic.de)
 * @version $Revision: 1.12 $
 */
public class XSLTServlet extends HttpServlet
{
   // Static filal --------------------------------------------------
   public static final String STACK_NAME = "webwork.result";
   public static final String ROOT_ELEMENT_NAME = "result";

   // Private -------------------------------------------------------
   private ErrorListener errorListener;
   private Map templatesCache;
   private XMLWalker xmlWalker;
   private Category log= Category.getInstance(getClass());;
 
   public void init(ServletConfig config) throws ServletException
   {
      super.init(config);

      templatesCache = new HashMap();
      xmlWalker = new XMLWalker();
      errorListener = new ErrorListenerImpl();
   }

   public void service(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException, java.net.MalformedURLException
   {
      try
      {
         long startTime = 0;
         if (log.isDebugEnabled())
            startTime = System.currentTimeMillis();

         // Get root object (bean)
         Object result = ValueStack.getStack(request).findValue(".");
         if (result == null)
         {
            log.info("Warning: Request attribute \"" + STACK_NAME + "\" is null!");
            result= "Warning: Request attribute \"" + STACK_NAME + "\" is null!";
         }

         // Create a transformer for the stylesheet.
         Templates   templates   = getTemplates(request);
         Transformer transformer = templates.newTransformer();

         String mimeType = templates.getOutputProperties().getProperty(OutputKeys.MEDIA_TYPE);
         if (mimeType == null)
         {
            // guess (this is a servlet, so text/html might be the best guess)
            mimeType = "text/html";
         }
         response.setContentType(mimeType);

         // Note that in this case the XML encoding can not be processed!
         XMLReaderAdapter xmlResultAdapter = new XMLReaderAdapter(xmlWalker, result, ROOT_ELEMENT_NAME);
         Source xmlSource = new SAXSource(xmlResultAdapter, new InputSource());

         // Transform the source XML to System.out.
         PrintWriter out = response.getWriter();
         transformer.transform(xmlSource, new StreamResult(out));

         out.close(); // ...and flush...
         if (log.isDebugEnabled())
            log.debug("Time:" + (System.currentTimeMillis() - startTime) + "ms");
      }
      catch (TransformerConfigurationException e)
      {
         throw new ServletException("XSLT Transformation creation failed: " + e, e);
      }
      catch (TransformerException e)
      {
         throw new ServletException("XSLT Transformation failed: " + e, e);
      }
   }

   protected Templates getTemplates(HttpServletRequest request)
            throws TransformerException, java.io.IOException
   {
      String path = getServletContext().getRealPath(request.getServletPath());
      if (path==null)
      {
         throw new TransformerException("Stylesheet " + path + " not found");
      }

      Templates templates = (Templates)templatesCache.get(path);
      if (templates==null)
      {
         synchronized(templatesCache)
         {
            // This may result in the template being put into the cache multiple times
            // if concurrent requests are made, but that's ok.
            log.debug("Preparing new XSLT stylesheet: " + path);
            TransformerFactory factory = TransformerFactory.newInstance();
	    factory.setErrorListener(errorListener);
            templates = factory.newTemplates(new StreamSource(new File(path)));
            templatesCache.put(path, templates);
         }
      }
      return templates;
   }
   
   
   private class ErrorListenerImpl implements ErrorListener{
	         public void warning(TransformerException exception) throws TransformerException
	         {
	            log.info("Warning: "+exception.getMessageAndLocation());
	         }
	
	         public void error(TransformerException exception) throws TransformerException
	         {
	            log.error("Error: "+exception.getMessageAndLocation());
	         }
	
	         public void fatalError(TransformerException exception) throws TransformerException
	         {
	            log.fatal("Fatal error: "+exception.getMessageAndLocation());
	            throw exception;
	        }
   }
   
      
}
