All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xmlcalabash.core.XProcException Maven / Gradle / Ivy

The newest version!
/*
 * XProcException.java
 *
 * Copyright 2008 Mark Logic Corporation.
 * Portions Copyright 2007 Sun Microsystems, Inc.
 * All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common
 * Development and Distribution License("CDDL") (collectively, the
 * "License"). You may not use this file except in compliance with the
 * License. You can obtain a copy of the License at
 * https://xproc.dev.java.net/public/CDDL+GPL.html or
 * docs/CDDL+GPL.txt in the distribution. See the License for the
 * specific language governing permissions and limitations under the
 * License. When distributing the software, include this License Header
 * Notice in each file and include the License file at docs/CDDL+GPL.txt.
 */

package com.xmlcalabash.core;

import com.xmlcalabash.model.Step;
import com.xmlcalabash.runtime.XStep;
import com.xmlcalabash.util.S9apiUtils;
import com.xmlcalabash.util.TreeWriter;
import com.xmlcalabash.util.URIUtils;

import net.sf.saxon.expr.instruct.Actor;
import net.sf.saxon.expr.instruct.AttributeSet;
import net.sf.saxon.expr.instruct.GlobalVariable;
import net.sf.saxon.expr.instruct.Instruction;
import net.sf.saxon.expr.instruct.NamedTemplate;
import net.sf.saxon.expr.instruct.TemplateRule;
import net.sf.saxon.expr.instruct.TerminationException;
import net.sf.saxon.expr.instruct.UserFunction;
import net.sf.saxon.expr.parser.ExplicitLocation;
import net.sf.saxon.expr.parser.XPathParser;
import net.sf.saxon.expr.XPathContext;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.lib.StandardErrorListener;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.XdmNode;
import net.sf.saxon.trace.ContextStackFrame;
import net.sf.saxon.trace.ContextStackIterator;
import net.sf.saxon.trans.KeyDefinition;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.AttributeLocation;
import net.sf.saxon.tree.util.Navigator;
import net.sf.saxon.type.ValidationException;

import javax.xml.transform.dom.DOMLocator;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.TransformerException;

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

/**
 *
 * @author ndw
 * @author bertfrees
 */
public class XProcException extends RuntimeException {

    public static final QName err_E0001 = new QName(XProcConstants.NS_XPROC_ERROR_EX, "XE0001"); // invalid pipeline
    public static final QName err_E0002 = new QName(XProcConstants.NS_XPROC_ERROR_EX, "XE0002"); // invalid configuration

    private static final String NS_DAISY_PIPELINE_XPROC = "http://www.daisy.org/ns/pipeline/xproc";
    private static final QName c_error = new QName("c", XProcConstants.NS_XPROC_STEP, "error");
    private static final QName _href = new QName("", "href");
    private static final QName _line = new QName("", "line");
    private static final QName _column = new QName("", "column");
    private static final QName _code = new QName("", "code");
    private static final QName _type = new QName("", "type");
    private static final QName _name = new QName("", "name");
    private static final QName px_cause = new QName("px", NS_DAISY_PIPELINE_XPROC, "cause");
    private static final QName px_location = new QName("px", NS_DAISY_PIPELINE_XPROC, "location");
    private static final QName px_file = new QName("px", NS_DAISY_PIPELINE_XPROC, "file");

    private final QName errorCode;
    private final XdmNode errorContent;
    private final XProcException errorCause;
    private final SourceLocator[] location;

    private static final SourceLocator NO_LOCATOR = new SourceLocator() {
            public String getPublicId() { return null; }
            public String getSystemId() { return null; }
            public int getLineNumber() { return -1; }
            public int getColumnNumber() { return -1; }
        };
    private static final SourceLocator[] NO_LOCATION = new SourceLocator[]{NO_LOCATOR};

    /**
     * Create an XProc error
     *
     * @param code     The type of the error, or null if untyped.
     * @param location The location of the error, or null if unknown or not
     *                 applicable. Can be a {@link SourceLocator[]}, {@link XStep}, {@link Step},
     *                 {@link XdmNode}, {@link TransformerException} or {@link Throwable}.
     * @param message  The content of the error. Can be a {@link String}, {@link XdmNode}, {@link
     *                 Throwable} or null if absent. In case of {@link Throwable}, this
     *                 argument determines both the content of the error (through its {@link
     *                 XProcException#getErrorContent()} or {@link Throwable#getMessage()} method)
     *                 and also the cause of the Java exception. Note that the latter is different
     *                 from the cause argument (see below).
     * @param cause    The XProc error that caused this XProc error to be created. Note that this
     *                 is not the same as the cause of the Java exception.
     */
    protected XProcException(QName code, Object location, Object message, XProcException cause) {
        super(
            message instanceof String
                ? (String)message
                : message instanceof XdmNode
                    ? ((XdmNode)message).getStringValue()
                    : message instanceof Throwable
                        ? ((Throwable)message).getMessage()
                        : null,
            message instanceof Throwable
                ? (Throwable)message
                : null);
        errorCode = code;
        if (!(message == null ||
              message instanceof String ||
              message instanceof XdmNode ||
              message instanceof Throwable))
            throw new IllegalStateException("coding error");
        errorContent = message instanceof XdmNode
            ? (XdmNode)message
            : message instanceof XProcException
                ? ((XProcException)message).getErrorContent()
                : null;
        this.location = getLocation(location);
        this.errorCause = cause;
    }

    private static class StaticXProcError extends XProcException {
        private StaticXProcError(Integer code, Object location, Object message) {
            super(code != null ? XProcConstants.staticError(code) : null,
                  location,
                  message,
                  null);
            if (code == null ||
                !(message == null ||
                  message instanceof String ||
                  message instanceof Throwable))
                throw new IllegalStateException("coding error");
        }
    }

    private static class DynamicXProcError extends XProcException {
        private DynamicXProcError(Object code, Object location, Object message, XProcException cause) {
            super(code instanceof QName
                      ? (QName)code
                      : code instanceof Integer
                          ? code != null
                              ? XProcConstants.dynamicError((Integer)code)
                              : null
                          : null,
                  location,
                  message,
                  cause);
            if (code == null ||
                !(code instanceof Integer ||
                  code instanceof QName))
                throw new IllegalStateException("coding error");
        }
    }

    public static XProcException staticError(int code) {
        return new StaticXProcError(code, null, null);
    }

    public static XProcException staticError(int code, String message) {
        return new StaticXProcError(code, null, message);
    }

    public static XProcException staticError(int code, XdmNode location, String message) {
        return new StaticXProcError(code, location, message);
    }

    public static XProcException staticError(int code, XdmNode location, Throwable message) {
        return new StaticXProcError(code, location, message);
    }

    public static XProcException dynamicError(int code) {
        return new DynamicXProcError(code, null, null, null);
    }

    public static XProcException dynamicError(int code, String message) {
        return new DynamicXProcError(code, null, message, null);
    }

    public static XProcException dynamicError(int code, Throwable message) {
        return new DynamicXProcError(code, null, message, null);
    }

    public static XProcException dynamicError(int code, XdmNode location, String message) {
        return new DynamicXProcError(code, location, message, null);
    }

    public static XProcException dynamicError(int code, XdmNode location, Throwable message) {
        return new DynamicXProcError(code, location, message, null);
    }

    public static XProcException dynamicError(int code, Step location) {
        return new DynamicXProcError(code, location, null, null);
    }

    public static XProcException dynamicError(int code, Step location, String message) {
        return new DynamicXProcError(code, location, message, null);
    }

    public static XProcException dynamicError(int code, XStep location, String message) {
        return new DynamicXProcError(code, location, message, null);
    }

    public static XProcException stepError(int code) {
        return new DynamicXProcError(XProcConstants.stepError(code), null, null, null);
    }

    public static XProcException stepError(int code, String message) {
        return new DynamicXProcError(XProcConstants.stepError(code), null, message, null);
    }

    public static XProcException stepError(int code, Throwable message) {
        return new DynamicXProcError(XProcConstants.stepError(code), null, message, null);
    }

    public static XProcException stepError(int code, XdmNode location, String message) {
        return new DynamicXProcError(XProcConstants.stepError(code), location, message, null);
    }

    public static XProcException stepError(int code, XStep location) {
        return new DynamicXProcError(XProcConstants.stepError(code), location, null, null);
    }

    public static XProcException stepError(int code, XStep location, Throwable message) {
        return new DynamicXProcError(XProcConstants.stepError(code), location, message, null);
    }

    public XProcException(QName code, XdmNode location, String message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, XStep location, String message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, XStep location, XdmNode message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, XStep location, Throwable message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, TransformerException location, XdmNode message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, TransformerException location, Throwable message) {
        this(code, location, message, null);
    }

    public XProcException(QName code, TransformerException location, XdmNode message, XProcException cause) {
        this(code, (Object)location, (Object)message, cause);
    }

    public XProcException(QName code, TransformerException location, Throwable message, XProcException cause) {
        this(code, (Object)location, (Object)message, cause);
    }

    public XProcException(QName code, XStep location, String message, XProcException cause) {
        this(code, (Object)location, (Object)message, cause);
    }

    public XProcException(QName code, XStep location, Throwable message, XProcException cause) {
        this(code, (Object)location, (Object)message, cause);
    }

    public XProcException(QName code, String message) {
        this(code, null, message, null);
    }

    public XProcException(QName code, Throwable message) {
        this(code, (Object)null, message, null);
    }

    public XProcException(String message) {
        this(null, null, message, null);
    }

    public XProcException(Throwable message) {
        this(null, (Object)null, message, null);
    }

    public XProcException(Throwable message, XProcException cause) {
        this(null, (Object)null, message, cause);
    }

    public XProcException(SourceLocator[] location, Throwable message) {
        this(null, location, message, null);
    }

    public XProcException(XdmNode location, String message) {
        this(null, location, message, null);
    }

    public XProcException(XdmNode location, Throwable message) {
        this(null, location, message, null);
    }

    public XProcException(Step location, String message) {
        this(null, location, message, null);
    }

    public XProcException(XStep location, String message) {
        this(null, location, message, null);
    }

    public XProcException(XStep location, Throwable message) {
        this(null, location, message, null);
    }

    public XProcException(XStep location, String message, XProcException cause) {
        this(null, location, message, cause);
    }

    public XProcException(XStep location, Throwable message, XProcException cause) {
        this(null, location, message, cause);
    }

    public XProcException(TransformerException location, XdmNode message) {
        this(null, location, message, null);
    }

    public XProcException(TransformerException location, Throwable message) {
        this(null, location, message, null);
    }

    public XProcException(TransformerException location, XdmNode message, XProcException cause) {
        this(null, location, message, cause);
    }

    public XProcException(TransformerException location, Throwable message, XProcException cause) {
        this(null, location, message, cause);
    }

    /**
     * Create a XProc error from a Java exception. The exception is used for both the message and
     * location or the XProc error. The exception's cause is also converted to an XProc error and
     * becomes the error cause (and this recursively).
     */
    public static XProcException fromException(Throwable throwable) {
        XProcException cause = throwable.getCause() != null
            ? fromException(throwable.getCause())
            : null;
        return new XProcException(null, throwable, throwable, cause);
    }

    /**
     * Create a new instance of the same XProc error, to allow to better track where exceptions are
     * thrown.
     */
    public XProcException copy() {
        return new XProcException(errorCode, location, this, errorCause);
    }

    public XProcException rebase(SourceLocator[] base) {
        return rebase(base, null);
    }

    public XProcException rebase(XStep base) {
        return rebase(base, null);
    }

    public XProcException rebase(TransformerException base) {
        return rebase(base, null);
    }

    public XProcException rebase(SourceLocator[] newBase, StackTraceElement[] oldBase) {
        return rebase((Object)newBase, (Object)oldBase);
    }

    private XProcException rebase(Object newBaseObject, Object oldBaseObject) {
        SourceLocator[] newBase = getLocation(newBaseObject);
        SourceLocator[] oldBase = getLocation(oldBaseObject);
        SourceLocator[] newLocation; {
            int newLength = 0;
            if (location != NO_LOCATION) {
                if (oldBase != NO_LOCATION) {
                    int m = location.length - 1;
                    int n = oldBase.length - 1;
                    while (m >= 0 && n >= 0
                           && Objects.equals(location[m].getSystemId(), oldBase[n].getSystemId())
                           && Objects.equals(location[m].getLineNumber(), oldBase[n].getLineNumber())
                           && Objects.equals(location[m].getColumnNumber(), oldBase[n].getColumnNumber())) {
                        m--;
                        n--;
                    }
                    // allow top frame to differ in line number, as long as we're in the same method
                    if (m >= 0 && n >= 0
                        && location[m] instanceof JavaFrame
                        && oldBase[n] instanceof JavaFrame) {
                        StackTraceElement frame = ((JavaFrame)location[m]).frame;
                        StackTraceElement oldFrame = ((JavaFrame)oldBase[n]).frame;
                        if (frame.getClassName().equals(oldFrame.getClassName())
                            && Objects.equals(frame.getMethodName(), oldFrame.getMethodName())
                            && Objects.equals(frame.getFileName(), oldFrame.getFileName())) {
                            m--;
                            n--;
                        }
                    }
                    if (n < 0)
                        newLength += (m + 2);
                    else
                        newLength += location.length;
                } else
                    newLength += location.length;
            }
            if (newBase != NO_LOCATION) {
                newLength += newBase.length;
            }
            if (newLength == 0)
                newLocation = NO_LOCATION;
            else {
                newLocation = new SourceLocator[newLength];
                int i = 0;
                if (newBase != NO_LOCATION)
                    newLength -= newBase.length;
                while (i < newLength) {
                    newLocation[i] = location[i];
                    i++;
                }
                if (newBase != NO_LOCATION) {
                    for (SourceLocator l : newBase)
                        newLocation[i++] = l;
                }
            }
        }
        XProcException newErrorCause = errorCause != null
            ? errorCause.rebase(newBase, oldBase)
            : null;
        return new XProcException(errorCode, newLocation, this, newErrorCause);
    }

    public QName getErrorCode() {
        return errorCode;
    }

    public XdmNode getErrorContent() {
        return errorContent;
    }

    public XProcException getErrorCause() {
        return errorCause;
    }

    public SourceLocator[] getLocation() {
        return location;
    }

    public static SourceLocator prettyLocator(SourceLocator locator, final String instructionName) {
        return new SourceLocatorWithInstructionName(locator) {
            protected String getInstructionName() {
                return instructionName;
            }
        };
    }

    public static SourceLocator prettyLocator(final String systemId, final int lineNumber, final int columnNumber,
                                              final String instructionName) {
        return new SourceLocatorWithInstructionName(new SourceLocator() {
                public String getPublicId() { return null; }
                public String getSystemId() { return systemId; }
                public int getLineNumber() { return lineNumber; }
                public int getColumnNumber() { return columnNumber; }
            }) {
            protected String getInstructionName() {
                return instructionName;
            }
        };
    }

    private static SourceLocator[] getLocation(Object object) {
        SourceLocator[] location; {
            if (object == null)
                location = null;
            else if (object instanceof SourceLocator[])
                location = (SourceLocator[])object;
            else if (object instanceof XStep)
                location = ((XStep)object).getLocation();
            else if (object instanceof Step)
                location = new SourceLocator[]{getLocator(((Step)object))};
            else if (object instanceof XdmNode)
                location = new SourceLocator[]{getLocator((XdmNode)object)};
            else if (object instanceof XProcException)
                location = ((XProcException)object).getLocation();
            else if (object instanceof TransformerException)
                location = getLocation((TransformerException)object);
            else if (object instanceof StackTraceElement[])
                location = getLocation((StackTraceElement[])object);
            else if (object instanceof Throwable)
                location = getLocation(((Throwable)object).getStackTrace());
            else
                throw new IllegalStateException("coding error");
        }
        if (location == null || location.length == 0)
            return NO_LOCATION;
        else
            return location;
    }

    private static SourceLocator[] getLocation(TransformerException e) {

        // This code is inspired by StandardErrorListener
        List frames = new ArrayList();
        SourceLocator loc = e.getLocator();
        if (loc == null) {
            TransformerException err = e;
            while (loc == null) {
                if (err.getException() instanceof TransformerException) {
                    err = (TransformerException)err.getException();
                    loc = err.getLocator();
                } else if (err.getCause() instanceof TransformerException) {
                    err = (TransformerException)err.getCause();
                    loc = err.getLocator();
                } else {
                    break;
                }
            }
        }
        if (loc == null)
            loc = ExplicitLocation.UNKNOWN_LOCATION;
        if (loc instanceof XPathParser.NestedLocation)
            loc = ((XPathParser.NestedLocation)loc).getContainingLocation();
        String instructionName = getInstructionName(loc);
        if (instructionName == null && e instanceof TerminationException)
            instructionName = "xsl:message";
        loc = prettyLocator(loc, instructionName);
        frames.add(loc);

        // now add more frames based on XPathContext
        if (e instanceof XPathException) {
            XPathException xe = (XPathException)e;
            XPathContext ctxt = xe.getXPathContext();
            if (ctxt != null) {
                Iterator ff = new ContextStackIterator(xe.getXPathContext());
                while (ff.hasNext()) {
                    ContextStackFrame f = ff.next();
                    instructionName = getInstructionName(f);
                    if (instructionName != null)
                        frames.add(prettyLocator(f.getSystemId(), f.getLineNumber(), -1, instructionName));
                }
            }
        }
        return frames.toArray(new SourceLocator[frames.size()]);
    }

    private static SourceLocator[] getLocation(StackTraceElement[] trace) {
        SourceLocator[] location = new SourceLocator[trace.length];
        for (int i = 0; i < trace.length; i++) {
            location[i] = new JavaFrame(trace[i]);
        }
        return location;
    }

    private static SourceLocator getLocator(XdmNode node) {
        if (node == null)
            return NO_LOCATOR;
        final String systemId = URIUtils.cwdAsURI().relativize(node.getBaseURI()).toASCIIString();
        final int line = node.getLineNumber() > 0 ? node.getLineNumber() : S9apiUtils.getDocumentElement(node).getLineNumber();
        final int col = node.getColumnNumber();
        return new SourceLocator() {
            public String getPublicId() {
                return null;
            }
            public String getSystemId() {
                return systemId;
            }
            public int getLineNumber() {
                return line;
            }
            public int getColumnNumber() {
                return col;
            }
            @Override
            public String toString() {
                StringBuilder s = new StringBuilder();
                String fileName = getSystemId();
                if (fileName != null && !"".equals(fileName)) {
                    if (fileName.lastIndexOf('/') >= 0)
                        fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
                    s.append(fileName);
                    int line = getLineNumber();
                    if (line > 0)
                        s.append(":" + getLineNumber());
                }
                if (s.length() == 0)
                    s.append("?");
                return s.toString();
            }
        };
    }

    public static SourceLocator getLocator(Step step) {
        return new XProcLocator(step);
    }

    private static String getInstructionName(SourceLocator loc) {
        if (loc instanceof AttributeLocation) {
            return ((AttributeLocation)loc).getElementName().getDisplayName() + "/@"
                + ((AttributeLocation)loc).getAttributeName();
        } else if (loc instanceof DOMLocator) {
            return ((DOMLocator)loc).getOriginatingNode().getNodeName();
        } else if (loc instanceof NodeInfo) {
            return ((NodeInfo)loc).getDisplayName();
        } else if (loc instanceof ValidationException && ((ValidationException)loc).getNode() != null) {
            return (((ValidationException)loc).getNode()).getDisplayName();
        } else if (loc instanceof Instruction) {
            return StandardErrorListener.getInstructionName((Instruction)loc);
        } else if (loc instanceof Actor) {
            return getInstructionName((Actor)loc);
        } else {
            return null;
        }
    }

    private static String getInstructionName(ContextStackFrame frame) {
        if (frame instanceof ContextStackFrame.FunctionCall) {
            StructuredQName name = ((ContextStackFrame.FunctionCall)frame).getFunctionName();
            if (name != null)
                return name.getClarkName() + "()";
        } else if (frame instanceof ContextStackFrame.ApplyTemplates) {
            String name = "xsl:apply-templates";
            Item node = frame.getContextItem();
            if (node instanceof NodeInfo)
                name += " processing " + Navigator.getPath((NodeInfo)node);
            return name;
        } else if (frame instanceof ContextStackFrame.CallTemplate) {
            return "xsl:call-template name=\""
                + ((ContextStackFrame.CallTemplate)frame).getTemplateName().getDisplayName() + "\"";
        } else if (frame instanceof ContextStackFrame.VariableEvaluation) {
            Object container = frame.getContainer();
            if (container instanceof Actor) {
                return getInstructionName((Actor)container);
            } else if (container instanceof TemplateRule) {
                return "xsl:template match=\"" + ((TemplateRule)container).getMatchPattern().toString() + "\"";
            }
        }
        return null;
    }

    private static String getInstructionName(Actor actor) {
        StructuredQName name = actor.getObjectName();
        String objectName = name == null ? "" : name.getDisplayName();
        if (actor instanceof UserFunction) {
            return "function " + objectName + "()";
        } else if (actor instanceof NamedTemplate) {
            return "template name=\"" + objectName + "\"";
        } else if (actor instanceof AttributeSet) {
            return "attribute-set " + objectName;
        } else if (actor instanceof KeyDefinition) {
            return "key " + objectName;
        } else if (actor instanceof GlobalVariable) {
            StructuredQName qName = ((GlobalVariable)actor).getVariableQName();
            if (qName.hasURI(NamespaceConstant.SAXON_GENERATED_VARIABLE)) {
                return "optimizer-created global variable";
            } else {
                return "global variable $" + qName.getDisplayName();
            }
        } else {
            return "procedure " + objectName;
        }
    }

    private static abstract class SourceLocatorWithInstructionName implements SourceLocator {

        private final SourceLocator locator;

        public SourceLocatorWithInstructionName(SourceLocator locator) {
            this.locator = locator;
        }

        public String getPublicId() {
            return locator != null ? locator.getPublicId() : null;
        }

        public String getSystemId() {
            return locator != null ? locator.getSystemId() : null;
        }

        public int getLineNumber() {
            return locator != null ? locator.getLineNumber() : -1;
        }

        public int getColumnNumber() {
            return locator != null ? locator.getColumnNumber() : -1;
        }

        protected abstract String getInstructionName();

        @Override
        public String toString() {
            StringBuilder s = new StringBuilder();
            String instructionName = getInstructionName();
            String fileName = getSystemId();
            if (fileName != null && !"".equals(fileName)) {
                if (fileName.lastIndexOf('/') >= 0)
                    fileName = fileName.substring(fileName.lastIndexOf('/') + 1);
                s.append(fileName);
                int line = getLineNumber();
                if (line > 0)
                    s.append(":" + getLineNumber());
            }
            if (instructionName != null && !"".equals(instructionName)) {
                if (s.length() > 0)
                    s.insert(0, instructionName + "(").append(")");
                else
                    s.append(instructionName);
            } else if (s.length() == 0)
                s.append("?");
            return s.toString();
        }
    }

    public static class XProcLocator extends SourceLocatorWithInstructionName {
        private final Step step;
        private final String instructionName;
        public XProcLocator(Step step) {
            super(getLocator(step != null ? step.getNode() : null));
            this.step = step;
            if (step == null || step.getNode() == null) {
                instructionName = null;
            } else {
                String name = step.getName();
                instructionName = step.getNode().getNodeName().getClarkName()
                + ((name.startsWith("#") || name.startsWith("!")) ? "" : (" name=\"" + name + "\""));
            }
        }
        public Step getStep() {
            return step;
        }
        protected String getInstructionName() {
            return instructionName;
        }
    }

    private static class JavaFrame implements SourceLocator {
        public final StackTraceElement frame;
        public JavaFrame(StackTraceElement frame) {
            this.frame = frame;
        }
        public String getPublicId() {
            return null;
        }
        public String getSystemId() {
            return frame.getFileName();
        }
        public int getLineNumber() {
            return frame.getLineNumber();
        }
        public int getColumnNumber() {
            return -1;
        }
        @Override
        public String toString() {
            return frame.toString();
        }
    }

    // adapted from java.lang.Throwable
    private String printEnclosedLocation(SourceLocator[] enclosingLocation) {
        StringBuilder s = new StringBuilder();
        String message = getMessage();
        if (errorCode != null) {
            s.append("[").append(errorCode).append("]");
            if (message != null)
                s.append(" ");
        }
        if (message != null)
            s.append(message);
        else if (errorCode == null)
            s.append((String)null);
        int m = location.length - 1;
        int n = enclosingLocation.length - 1;
        while (m >= 0 && n >=0 && location[m].equals(enclosingLocation[n])) {
            m--;
            n--;
        }
        int inCommon = location.length - 1 - m;
        for (int i = 0; i <= m; i++)
            if (location[i] != NO_LOCATOR)
                s.append("\n\tat " + location[i]);
        if (inCommon != 0)
            s.append("\n\t... " + inCommon + " more");
        if (errorCause != null) {
            s.append("\nCaused by: ");
            s.append(errorCause.printEnclosedLocation(location));
        }
        return s.toString();
    }

    @Override
    public String toString() {
        return printEnclosedLocation(new SourceLocator[]{});
    }

    private static void serializeLocation(SourceLocator[] location, TreeWriter writer) {
        boolean empty = true;
        for (SourceLocator l : location) {
            if (l.getSystemId() != null || l.getLineNumber() > 0) {
                empty = false;
                break;
            }
        }
        if (empty) return;
        writer.addStartElement(px_location);
        writer.startContent();
        for (SourceLocator l : location) {
            if (l.getSystemId() != null || l.getLineNumber() > 0) {
                writer.addStartElement(px_file);
                if (l.getSystemId() != null)
                    writer.addAttribute(_href, l.getSystemId());
                int line = l.getLineNumber();
                if (line > 0)
                    writer.addAttribute(_line, ""+line);
                int column = l.getColumnNumber();
                if (column > 0)
                    writer.addAttribute(_column, ""+column);
                writer.addEndElement();
            }
        }
        writer.addEndElement();
    }

    public void serialize(TreeWriter writer) {
        writer.addStartElement(c_error);
        if (errorCode != null) {
            StructuredQName qCode = new StructuredQName(errorCode.getPrefix(), errorCode.getNamespaceURI(), errorCode.getLocalName());
            writer.addNamespace(qCode.getPrefix(), qCode.getNamespaceBinding().getURI());
            writer.addAttribute(_code, qCode.getDisplayName());
        }
        if (location[0] instanceof XProcLocator) {
            Step step = ((XProcLocator)location[0]).step;
            if (step != null) {
                writer.addAttribute(_name, step.getName());
                writer.addAttribute(_type, step.getType().toString());
            }
        }
        if (location[0].getSystemId() != null)
            writer.addAttribute(_href, location[0].getSystemId());
        if (location[0].getLineNumber() > 0)
            writer.addAttribute(_line, ""+location[0].getLineNumber());
        if (location[0].getColumnNumber() > 0)
            writer.addAttribute(_column, ""+location[0].getColumnNumber());
        writer.startContent();
        if (errorContent != null)
            writer.addSubtree(errorContent);
        else {
            String message = getMessage();
            if (message != null)
                writer.addText(message);
        }
        serializeLocation(location, writer);
        if (errorCause != null) {
            writer.addStartElement(px_cause);
            writer.startContent();
            errorCause.serialize(writer);
            writer.addEndElement();
        }
        writer.addEndElement();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy