package com.jclark.xsl.sax;

import org.xml.sax.*;
import com.jclark.xsl.tr.*;
import com.jclark.xsl.om.*;
import java.util.Locale;
import java.io.IOException;
import java.net.URL;
import java.util.Hashtable;

public class XSLProcessorImpl implements XSLProcessor, Cloneable, ParameterSet {
  private XMLProcessorEx sourceLoader;
  private XMLProcessorEx sheetLoader;
  private boolean engineMatchesSheetLoader = false;
  private Parser sheetParser;
  private Parser sourceParser;
  private Sheet sheet;
  private Engine engine;
  private InputSource sheetSource;
  private ResultBase result;
  private OutputMethodHandler outputMethodHandler;
  private DocumentHandler documentHandler;
  private ErrorHandler errorHandler;
  private Hashtable params = new Hashtable();

  public void setParser(Parser sourceParser, Parser sheetParser) {
    this.sourceParser = sourceParser;
    this.sheetParser = sheetParser;
    sourceLoader = new XMLProcessorImpl(sourceParser);
    if (sourceParser == sheetParser)
      sheetLoader = sourceLoader;
    else
      sheetLoader = new XMLProcessorImpl(sheetParser);
    engineMatchesSheetLoader = false;
  }

  public void setParser(XMLProcessorEx loader) {
    this.sourceParser = null;
    this.sheetParser = null;
    sourceLoader = loader;
    sheetLoader = loader;
    engineMatchesSheetLoader = false;
  }

  public void setParser(Parser parser) {
    setParser(parser, parser);
  }

  public void setLocale(Locale locale) throws SAXException {
    if (sheetParser != null)
      sheetParser.setLocale(locale);
    if (sourceParser != null)
      sourceParser.setLocale(locale);
  }

  public void setDTDHandler(DTDHandler handler) { }

  public void setEntityResolver(EntityResolver resolver) {
    if (sheetParser != null)
      sheetParser.setEntityResolver(resolver);
    if (sourceParser != null)
      sourceParser.setEntityResolver(resolver);
  }

  public void setOutputMethodHandler(OutputMethodHandler handler) {
    outputMethodHandler = handler;
    documentHandler = null;
  }

  public void setDocumentHandler(DocumentHandler handler) {
    documentHandler = handler;
    outputMethodHandler = null;
  }

  public void parse(String systemId) throws SAXException, IOException {
    parse(new InputSource(systemId));
  }

  public void setErrorHandler(ErrorHandler handler) {
    if (sheetParser != null)
      sheetParser.setErrorHandler(handler);
    if (sourceParser != null)
      sourceParser.setErrorHandler(handler);
    if (sheetLoader != null)
      sheetLoader.setErrorHandler(handler);
    if (sourceLoader != null)
      sourceLoader.setErrorHandler(handler);
    this.errorHandler = handler;
  }

  public void loadStylesheet(InputSource sheetSource) throws SAXException, IOException {
    if (!engineMatchesSheetLoader) {
      if (sheetLoader == null)
	throw new Error("loadStylesheet called before setParser");
      engine = new EngineImpl(sheetLoader,
			      new ExtensionHandlerImpl());
      engineMatchesSheetLoader = true;
    }
    try {
      phase(1);
      Node node = sheetLoader.load(sheetSource,
				   0,
				   engine.getSheetLoadContext(),
				   engine.getNameTable());
      phase(2);
      sheet = engine.createSheet(node);
    }
    catch (XSLException e) {
      handleXSLException(e);
    }
  }


  public void parse(InputSource source) throws SAXException, IOException {
    try {
      if (outputMethodHandler != null)
	result = new MultiNamespaceResult(outputMethodHandler, errorHandler);
      else if (documentHandler != null)
	result = new MultiNamespaceResult(documentHandler, errorHandler);
      else
	result = new MultiNamespaceResult(new HandlerBase(), errorHandler);
      phase(3);
      Node root = sourceLoader.load(source,
				    0,
				    sheet.getSourceLoadContext(),
				    engine.getNameTable());
      phase(4);
      sheet.process(root, sourceLoader, this, result);
      phase(5);
    }
    catch (XSLException e) {
      handleXSLException(e);
    }
  }

  void handleXSLException(XSLException e) throws SAXException, IOException {
    String systemId = null;
    int lineNumber = -1;
    Node node = e.getNode();
    if (node != null) {
      URL url = node.getURL();
      if (url != null)
	systemId = url.toString();
      lineNumber = node.getLineNumber();
    }
    Exception wrapped = e.getException();
    String message = e.getMessage();
    if (systemId != null || lineNumber != -1)
      throw new SAXParseException(message,
				  null,
				  systemId,
				  lineNumber,
				  -1,
				  wrapped);
    if (message == null) {
      if (wrapped instanceof SAXException)
	throw (SAXException)wrapped;
      if (wrapped instanceof IOException)
	throw (IOException)wrapped;
    }
    throw new SAXException(message, wrapped);
  }

  void phase(int n) { }

  public Object clone() {
    try {
       XSLProcessorImpl cloned = (XSLProcessorImpl)super.clone();
       cloned.params = (Hashtable)cloned.params.clone();
       return cloned;
    }
    catch (CloneNotSupportedException e) {
      throw new Error("unexpected CloneNotSupportedException");
    }
  }

  public Object getParameter(Name name) {
    String nameString = name.getNamespace();
    if (nameString == null)
      nameString = name.getLocalPart();
    else
      nameString = (nameString
		    + OutputMethodHandler.namespaceSeparator
		    + name.getLocalPart());
    return params.get(nameString);
  }

  public void setParameter(String name, Object obj) {
    params.put(name, obj);
  }
}
