/*
 * Written by Dawid Kurzyniec and released to the public domain, as explained
 * at http://creativecommons.org/licenses/publicdomain
 */

package edu.emory.mathcs.util.xml;

import org.xml.sax.*;
import java.io.*;
import java.util.*;

/**
 * SAX error handler that remembers all errors and warnings encountered during
 * parsing and offers methods to access them after parsing is done.
 *
 * @author Dawid Kurzyniec
 * @version 1.0
 */
public class AccumulatingErrorHandler implements ErrorHandler {
    final ErrorHandler delegate;

    static final int FATAL_ERROR = 0x1;
    static final int ERROR       = 0x2;
    static final int WARNING     = 0x4;

    List problems;
    List problemTypes;
    int warningCount;
    int errorCount;
    int fatalCount;

    /**
     * Creates a new accumulating error handler.
     */
    public AccumulatingErrorHandler() {
        this(null);
    }

    /**
     * Creates a new accumulating error handler that, in addition to its
     * normal behavior, also delegates all events to the specified error
     * handler.
     */
    public AccumulatingErrorHandler(ErrorHandler delegate) {
        this.delegate = delegate;
    }

    public void warning(SAXParseException ex) throws SAXException {
        if (delegate != null) delegate.warning(ex);
        addProblem(WARNING, ex);
    }

    public void error(SAXParseException ex) throws SAXException {
        if (delegate != null) delegate.error(ex);
        addProblem(ERROR, ex);
    }

    public void fatalError(SAXParseException ex) throws SAXException {
        if (delegate != null) delegate.fatalError(ex);
        addProblem(FATAL_ERROR, ex);
    }

    private synchronized void addProblem(int type, SAXParseException ex) {
        if (problems == null) {
            problems = new ArrayList();
            problemTypes = new ArrayList();
        }
        switch (type) {
            case FATAL_ERROR: fatalCount++; break;
            case ERROR:       errorCount++; break;
            case WARNING:     warningCount++; break;
            default: assert false;
        }

        problems.add(ex);
        problemTypes.add(new Integer(type));
    }

    public boolean hadWarnings() {
        return (warningCount > 0);
    }

    public boolean hadAnyErrors() {
        return (errorCount + fatalCount > 0);
    }

    public boolean hadNonfatalErrors() {
        return (errorCount > 0);
    }

    public boolean hadFatalErrors() {
        return (fatalCount > 0);
    }

    public boolean hadAnyProblems() {
        return (warningCount + errorCount + fatalCount > 0);
    }

    public synchronized SAXParseException[] getWarnings() {
        return getProblems(WARNING);
    }

    public SAXParseException[] getAllErrors() {
        return getProblems(ERROR | FATAL_ERROR);
    }

    public SAXParseException[] getNonfatalErrors() {
        return getProblems(ERROR);
    }

    public SAXParseException[] getFatalErrors() {
        return getProblems(FATAL_ERROR);
    }

    public SAXParseException[] getAllProblems() {
        return getProblems(WARNING | ERROR | FATAL_ERROR);
    }

    private synchronized SAXParseException[] getProblems(int mask) {
        if (problems == null) return null;
        List list = new ArrayList();
        int len = problems.size();
        for (int i = 0; i < len; i++) {
            if ((((Integer)problemTypes.get(i)).intValue() & mask) != 0) {
                list.add(problems.get(i));
            }
        }
        return (SAXParseException[])list.toArray(new SAXParseException[list.size()]);
    }

    public String getDetails() {
        StringBuffer buf = new StringBuffer();
        addDetails(buf, "fatal error", getFatalErrors());
        addDetails(buf, "error", getNonfatalErrors());
        addDetails(buf, "warning", getWarnings());
        return buf.toString();
    }

    private static void addDetails(StringBuffer buf, String type,
                                   SAXParseException[] problems)
    {
        if (problems == null) return;
        for (int i = 0; i < problems.length; i++) {
            SAXParseException e = problems[i];
            buf.append("Line " ).append(e.getLineNumber()).append(": ").append(type).
                append(": ").append(e.toString()).append("\n");
        }
    }
};
