/*
 * Copyright (c) 1998-2006 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.jsp;

import java.io.*;
import java.util.*;
import java.util.logging.*;
import java.lang.ref.SoftReference;

import javax.servlet.*;
import javax.servlet.jsp.*;
import javax.servlet.http.*;

import javax.xml.transform.*;
import javax.xml.transform.dom.*;

import org.w3c.dom.*;

import com.caucho.log.Log;
import com.caucho.util.*;
import com.caucho.vfs.*;
import com.caucho.xml.*;
import com.caucho.xsl.*;

import com.caucho.loader.DynamicClassLoader;

import com.caucho.server.webapp.Application;

import com.caucho.server.connection.CauchoRequest;
import com.caucho.server.connection.CauchoResponse;

class XslManager {
  private static final Logger log = Log.open(XslManager.class);
  static final L10N L = new L10N(XslManager.class);

  private Application _application;
  private Path _workPath;
  private LruCache<String,SoftReference<Templates>> _xslCache =
    new LruCache<String,SoftReference<Templates>>(256);
  private boolean _strictXsl;
  private long _lastUpdate;

  public XslManager(ServletContext context)
  {
    _application = (Application) context;

    _workPath = CauchoSystem.getWorkPath();
  }

  public void setStrictXsl(boolean strictXsl)
  {
    _strictXsl = strictXsl;
  }

  public String getServletInfo()
  {
    return "Resin XTP";
  }

  Templates get(String href, CauchoRequest req)
    throws Exception
  {
    String servletPath = req.getPageServletPath();

    Application application = req.getApplication();

    Path appDir = application.getAppDir();
    Path pwd = appDir.lookupNative(application.getRealPath(servletPath));
    pwd = pwd.getParent();
    
    String fullStyleSheet = pwd.toString() + "/" + href;

    Templates stylesheet = null;

    long now = Alarm.getCurrentTime();

    SoftReference<Templates> templateRef = _xslCache.get(fullStyleSheet);

    if (templateRef != null)
      stylesheet = templateRef.get();

    if (stylesheet instanceof StylesheetImpl &&
        ! ((StylesheetImpl) stylesheet).isModified())
      return stylesheet;

    _lastUpdate = now;
    stylesheet = getStylesheet(req, href);

    if (stylesheet == null)
      throw new ServletException(L.l("can't find stylesheet `{0}'", href));

    _xslCache.put(fullStyleSheet, new SoftReference<Templates>(stylesheet));

    return stylesheet;
  }

  /**
   * Returns the stylesheet given by the references.
   */
  Templates getStylesheet(CauchoRequest req, String href)
    throws Exception
  {
    String servletPath = req.getPageServletPath();

    Application application = req.getApplication();
    Path appDir = application.getAppDir();
    Path pwd = appDir.lookupNative(application.getRealPath(servletPath));
    pwd = pwd.getParent();

    DynamicClassLoader loader;
    loader = (DynamicClassLoader) application.getClassLoader();
    
    MergePath stylePath = new MergePath();
    stylePath.addMergePath(pwd);
    stylePath.addMergePath(appDir);

    String resourcePath = loader.getResourcePathSpecificFirst();
    stylePath.addClassPath(resourcePath);
    
    Path hrefPath = stylePath.lookup(href);

    if (hrefPath.canRead()) {
      DynamicClassLoader owningLoader = getStylesheetLoader(href, hrefPath);

      if (owningLoader != null) {
	loader = owningLoader;
	
	stylePath = new MergePath();
	stylePath.addMergePath(pwd);
	stylePath.addMergePath(appDir);
	resourcePath = loader.getResourcePathSpecificFirst();
	stylePath.addClassPath(resourcePath);
      }
    }

    Thread thread = Thread.currentThread();
    ClassLoader oldLoader = thread.getContextClassLoader();
    try {
      thread.setContextClassLoader(loader);
    
      CauchoStylesheet xsl;

      AbstractStylesheetFactory factory;
    
      if (_strictXsl)
	factory = new Xsl();
      else
	factory = new StyleScript();

      factory.setStylePath(stylePath);
      factory.setClassLoader(loader);
      // factory.setWorkPath(_workPath);
    
      String className = "";

      if (pwd.lookup(href).canRead()) {
	int p = req.getServletPath().lastIndexOf('/');
	if (p >= 0)
	  className += req.getServletPath().substring(0, p);
      }
      /*
	else if (href.startsWith("/"))
	href = href.substring(1);
      */
      
      className += "/" + href;

      factory.setClassName(className);

      return factory.newTemplates(href);
    } finally {
      thread.setContextClassLoader(oldLoader);
    }
  }

  private DynamicClassLoader getStylesheetLoader(String href, Path sourcePath)
  {
    DynamicClassLoader owningLoader = null;
    ClassLoader loader = Thread.currentThread().getContextClassLoader();

    for (; loader != null; loader = loader.getParent()) {
      if (! (loader instanceof DynamicClassLoader))
	continue;

      DynamicClassLoader dynLoader = (DynamicClassLoader) loader;
      
      MergePath mp = new MergePath();
      String resourcePath = dynLoader.getResourcePathSpecificFirst();
      mp.addClassPath(resourcePath);

      Path loaderPath = mp.lookup(href);

      if (loaderPath.getNativePath().equals(sourcePath.getNativePath()))
	owningLoader = dynLoader;
    }

    return owningLoader;
  }
}

