package org.exist.xquery;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.exist.xquery.parser.XQueryAST;

public class XPathException extends Exception {

	private int line = 0;
	private int column = 0;
	private String message = null;
	private List callStack = null;
    
	/**
	 * @param message
	 */
	public XPathException(String message) {
		super();
		this.message = message;
	}

	public XPathException(XQueryAST ast, String message) {
		super();
		this.message = message;
		setASTNode(ast);
	}
	
	public XPathException(String message, int line, int column) {
		super();
		this.message = message;
		this.line = line;
		this.column = column;
	}
	
	/**
	 * @param cause
	 */
	public XPathException(Throwable cause) {
		super(cause);
	}

	/**
	 * @param message
	 * @param cause
	 */
	public XPathException(String message, Throwable cause) {
		super(cause);
		this.message = message;
	}

	public XPathException(XQueryAST ast, String message, Throwable cause) {
		super(cause);
		this.message = message;
		setASTNode(ast);
	}
	
	public int getLine() {
		return line;
	}
	
	public int getColumn() {
		return column;
	}
	
	public void setASTNode(XQueryAST ast) {
		if(ast != null) {
			this.line = ast.getLine();
			this.column = ast.getColumn();
		}
	}
	
    public void addFunctionCall(UserDefinedFunction def, XQueryAST ast) {
        if (callStack == null)
            callStack = new ArrayList();
        StringBuffer msg = new StringBuffer();
        msg.append(def.toString());
        msg.append(" [");
        msg.append(ast.getLine());
        msg.append(", ");
        msg.append(ast.getColumn());
        msg.append("]");
        callStack.add(msg.toString());
    }
	
    public void prependMessage(String msg) {
        message = msg + message;
    }
    
	/* (non-Javadoc)
	 * @see java.lang.Throwable#getMessage()
	 */
	public String getMessage() {
        StringBuffer buf = new StringBuffer();
		if(message == null)
			message = "";
		buf.append(message);
        if (getLine() > 0) {
			buf.append(" [at line ");
			buf.append(getLine());
			buf.append(", column ");
			buf.append(getColumn());
			buf.append("]");
		}
        if (callStack != null) {
            buf.append("\nIn call to function:\n");
            for (Iterator i = callStack.iterator(); i.hasNext(); ) {
                buf.append('\t').append(i.next());
                if (i.hasNext())
                    buf.append('\n');
            }
        }
        return buf.toString();
	}
    
    public String getMessageAsHTML() {
        StringBuffer buf = new StringBuffer();
        if(message == null)
            message = "";
		message = message.replaceAll("\r?\n", "<br/>");
        buf.append("<div class=\"message\">");
        buf.append("<h2>").append(message);
        if (getLine() > 0) {
            buf.append(" [at line ");
            buf.append(getLine());
            buf.append(", column ");
            buf.append(getColumn());
            buf.append("]");
        }
        buf.append("</h2>");
        if (callStack != null) {
            buf.append("<p>In call to function:</p>");
            buf.append("<ul class=\"trace\">");
            for (Iterator i = callStack.iterator(); i.hasNext(); ) {
                buf.append("<li>").append(i.next()).append("</li>");
            }
            buf.append("</ul>");
        }
        buf.append("</div>");
        return buf.toString();
    }
}

@@ -1447,267 +1447,55 @@
     
-StringBuffer msg = new StringBuffer();%0A        msg.append(def.toString());%0A        msg.append(%22 %5B%22);%0A        msg.append(ast.getLine());%0A        msg.append(%22, %22);%0A        msg.append(ast.getColumn());%0A        msg.append(%22%5D%22);%0A        callStack.add(msg.toString(
+callStack.add(new FunctionStackElement(def, ast
 ));%0A
@@ -2492,55 +2492,8 @@
 %22);%0A
-        buf.append(%22%3Cdiv class=%5C%22message%5C%22%3E%22);%0A
     
@@ -2838,33 +2838,32 @@
 d(%22%3C
-p%3EIn call to function:%3C/p
+table id=%5C%22xquerytrace%5C%22
 %3E%22);
@@ -2892,29 +2892,82 @@
 d(%22%3C
-ul class=%5C%22trace%5C%22%3E%22)
+caption%3EXQuery Stack Trace%3C/caption%3E%22);%0A            FunctionStackElement e
 ;%0A  
@@ -3053,56 +3053,300 @@
     
-buf.append(%22%3Cli%3E%22).append(i.next()).append(%22%3C/li
+e = (FunctionStackElement) i.next();%0A                buf.append(%22%3Ctr%3E%3Ctd class=%5C%22func%5C%22%3E%22).append(e.function).append(%22%3C/td%3E%22);%0A                buf.append(%22%3Ctd class=%5C%22lineinfo%5C%22%3E%22).append(e.ast.getLine()).append(':').append(e.ast.getColumn()).append(%22%3C/td%3E%22);%0A                buf.append(%22%3C/tr
 %3E%22);
@@ -3386,18 +3386,21 @@
 pend(%22%3C/
-ul
+table
 %3E%22);%0A   
@@ -3418,30 +3418,552 @@
     
-buf.append(%22%3C/div%3E%22);%0A
+return buf.toString();%0A    %7D%0A    %0A    private static class FunctionStackElement %7B%0A        String function;%0A        XQueryAST ast;%0A        %0A        FunctionStackElement(UserDefinedFunction func, XQueryAST ast) %7B%0A            this.function = func.toString();%0A            this.ast = ast;%0A        %7D%0A        %0A        public String toString() %7B%0A            StringBuffer buf = new StringBuffer();%0A            buf.append(function).append(%22 %5B%22);%0A            buf.append(ast.getLine()).append(%22:%22);%0A            buf.append(ast.getColumn()).append('%5D');%0A    
     
@@ -3997,8 +3997,17 @@
     
-%7D%0A
+    %7D%0A    
 %7D%0A
+%7D

Improve error messages displayed by XQueryServlet.