blob: 0ae6a7a95592e6b05388eda43e13d0857b4e1459 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id$
*/
/*
*
* LoggingSAXErrorHandler.java
*
*/
package org.apache.qetest.xsl;
import org.apache.qetest.Logger;
import org.apache.qetest.LoggingHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
//-------------------------------------------------------------------------
/**
* Cheap-o ErrorHandler for use by API tests.
* <p>Implements org.xml.sax.ErrorHandler and dumps everything to a Reporter.</p>
* @author shane_curcuru@lotus.com
* @version $Id$
*/
public class LoggingSAXErrorHandler extends LoggingHandler implements ErrorHandler
{
/** No-op ctor seems useful. */
public LoggingSAXErrorHandler()
{
setLogger(getDefaultLogger());
}
/**
* Ctor that calls setLogger automatically.
*
* @param l Logger we should log to
*/
public LoggingSAXErrorHandler(Logger l)
{
setLogger(l);
}
/**
* Constants determining when we should throw exceptions.
* <ul>Flags are combineable like a bitfield.
* <li>THROW_NEVER - never ever (always continue - note this
* may have unexpected effects when fatalErrors happen, see
* {@link javax.xml.transform.ErrorListener#fatalError(javax.xml.transform.TransformerException)}</li>
* <li>THROW_ON_WARNING - throw only on warnings</li>
* <li>THROW_ON_ERROR - throw only on errors</li>
* <li>THROW_ON_FATAL - throw only on fatalErrors - default</li>
* <li>THROW_ALWAYS - always throw exceptions</li>
* </ul>
*/
public static final int THROW_NEVER = 0;
/** THROW_ON_WARNING - throw only on warnings. */
public static final int THROW_ON_WARNING = 1;
/** THROW_ON_ERROR - throw only on errors. */
public static final int THROW_ON_ERROR = 2;
/** THROW_ON_FATAL - throw only on fatalErrors - default. */
public static final int THROW_ON_FATAL = 4;
/** THROW_ALWAYS - always throw exceptions. */
public static final int THROW_ALWAYS = THROW_ON_WARNING & THROW_ON_ERROR
& THROW_ON_FATAL;
/** If we should throw an exception for each message type. */
protected int throwWhen = THROW_ON_FATAL;
/**
* Tells us when we should re-throw exceptions.
*
* @param t THROW_WHEN_* constant as to when we should re-throw
* an exception when we are called
*/
public void setThrowWhen(int t)
{
throwWhen = t;
}
/**
* Tells us when we should re-throw exceptions.
*
* @return THROW_WHEN_* constant as to when we should re-throw
* an exception when we are called
*/
public int getThrowWhen()
{
return throwWhen;
}
/** Constant for items returned in getCounters: messages. */
public static final int TYPE_WARNING = 0;
/** Constant for items returned in getCounters: errors. */
public static final int TYPE_ERROR = 1;
/** Constant for items returned in getCounters: fatalErrors. */
public static final int TYPE_FATALERROR = 2;
/**
* Counters for how many events we've handled.
* Index into array are the TYPE_* constants.
*/
protected int[] counters =
{
0, /* warning */
0, /* error */
0 /* fatalError */
};
/**
* Get a list of counters of all items we've logged.
* Returned as warnings, errors, fatalErrors
* Index into array are the TYPE_* constants.
*
* @return array of int counters for each item we log
*/
public int[] getCounters()
{
return counters;
}
/** Prefixed to all logger msg output. */
public static final String prefix = "SEH:";
/**
* Really Cheap-o string representation of our state.
*
* @return String of getCounters() rolled up in minimal space
*/
public String getQuickCounters()
{
return (prefix + "(" + counters[TYPE_WARNING] + ", "
+ counters[TYPE_ERROR] + ", " + counters[TYPE_FATALERROR] + ")");
}
/** Cheap-o string representation of last warn/error/fatal we got. */
protected String lastItem = NOTHING_HANDLED;
/**
* Sets a String representation of last item we handled.
*
* @param s set into lastItem for retrieval with getLast()
*/
protected void setLastItem(String s)
{
lastItem = s;
}
/**
* Get a string representation of last item we logged.
*
* @return String of the last item handled
*/
public String getLast()
{
return lastItem;
}
/** Expected values for events we may handle, default=ITEM_DONT_CARE. */
protected String[] expected =
{
ITEM_DONT_CARE, /* warning */
ITEM_DONT_CARE, /* error */
ITEM_DONT_CARE /* fatalError */
};
/**
* Ask us to report checkPass/Fail for certain events we handle.
* Since we may have to handle many events between when a test
* will be able to call us, testers can set this to have us
* automatically call checkPass when we see an item that matches,
* or to call checkFail when we get an unexpected item.
* Generally, we only call check* methods when:
* <ul>
* <li>containsString is not set, reset, or is ITEM_DONT_CARE,
* we do nothing (i.e. never call check* for this item)</li>
* <li>containsString is ITEM_CHECKFAIL, we will always call
* checkFail with the contents of any item if it occours</li>
* <li>containsString is anything else, we will grab a String
* representation of every item of that type that comes along,
* and if the containsString is found, case-sensitive, within
* the handled item's string, call checkPass, otherwise
* call checkFail</li>
* <ul>
* Note that any time we handle a particular event that was
* expected, we un-set the expected value for that item. This
* means that you can only ask us to validate one occourence
* of any particular event; all events after that one will
* be treated as ITEM_DONT_CARE. Callers can of course call
* setExpected again, of course, but this covers the case where
* we handle multiple events in a single block, perhaps out of
* the caller's direct control.
* Note that we first store the event via setLast(), then we
* validate the event as above, and then we potentially
* re-throw the exception as by setThrowWhen().
*
* @param itemType which of the various types of items we might
* handle; should be defined as a constant by subclasses
* @param containsString a string to look for within whatever
* item we handle - usually checked for by seeing if the actual
* item we handle contains the containsString
*/
public void setExpected(int itemType, String containsString)
{
// Default to don't care on null
if (null == containsString)
containsString = ITEM_DONT_CARE;
try
{
expected[itemType] = containsString;
}
catch (ArrayIndexOutOfBoundsException aioobe)
{
// Just log it for callers reference and continue anyway
logger.logMsg(level, prefix + " setExpected called with illegal type:" + itemType);
}
}
/**
* Reset all items or counters we've handled.
*/
public void reset()
{
setLastItem(NOTHING_HANDLED);
for (int i = 0; i < counters.length; i++)
{
counters[i] = 0;
}
for (int j = 0; j < expected.length; j++)
{
expected[j] = ITEM_DONT_CARE;
}
}
/**
* Grab basic info out of a SAXParseException.
*
* @param exception the SAXParseException to get info from
* @return condensed string of important info therefrom
*/
public String getParseExceptionInfo(SAXParseException exception)
{
if (exception == null)
return null;
String retVal = new String("");
String tmp;
tmp = exception.getPublicId();
if (tmp != null)
retVal += " publicID:" + tmp;
tmp = exception.getSystemId();
if (tmp != null)
retVal += " systemId:" + tmp;
try
{
tmp = Integer.toString(exception.getLineNumber());
}
catch (NumberFormatException nfe)
{
tmp = null;
}
if (tmp != null)
retVal += " lineNumber:" + tmp;
try
{
tmp = Integer.toString(exception.getColumnNumber());
}
catch (NumberFormatException nfe)
{
tmp = null;
}
if (tmp != null)
retVal += " columnNumber:" + tmp;
tmp = exception.getMessage(); // Will grab inner message if needed
if (tmp != null)
retVal += " message:" + tmp;
return retVal;
}
/////////////////// Implement SAXErrorHandler ///////////////////
/**
* Implementation of warning; calls logMsg with info contained in exception.
*
* @param exception provided by Transformer
* @exception TransformerException thrown only if asked to or if loggers are bad
*/
public void warning(SAXParseException exception) throws SAXException
{
// Increment counter and save the exception
counters[TYPE_WARNING]++;
String exInfo = getParseExceptionInfo(exception);
setLastItem(exInfo);
// Log or validate the exception
logOrCheck(TYPE_WARNING, "warning", exInfo);
// Also re-throw the exception if asked to
if ((throwWhen & THROW_ON_WARNING) == THROW_ON_WARNING)
{
throw new SAXException(exception);
}
}
/**
* Implementation of error; calls logMsg with info contained in exception.
* Only ever throws an exception itself if asked to or if loggers are bad.
*
* @param exception provided by Transformer
* @exception TransformerException thrown only if asked to or if loggers are bad
*/
public void error(SAXParseException exception) throws SAXException
{
// Increment counter, save the exception, and log what we got
counters[TYPE_ERROR]++;
String exInfo = getParseExceptionInfo(exception);
setLastItem(exInfo);
// Log or validate the exception
logOrCheck(TYPE_ERROR, "error", exInfo);
// Also re-throw the exception if asked to
if ((throwWhen & THROW_ON_ERROR) == THROW_ON_ERROR)
{
throw new SAXException(exception);
}
}
/**
* Implementation of error; calls logMsg with info contained in exception.
* Only ever throws an exception itself if asked to or if loggers are bad.
* Note that this may cause unusual behavior since we may not actually
* re-throw the exception, even though it was 'fatal'.
*
* @param exception provided by Transformer
* @exception TransformerException thrown only if asked to or if loggers are bad
*/
public void fatalError(SAXParseException exception) throws SAXException
{
// Increment counter, save the exception, and log what we got
counters[TYPE_FATALERROR]++;
String exInfo = getParseExceptionInfo(exception);
setLastItem(exInfo);
// Log or validate the exception
logOrCheck(TYPE_FATALERROR, "fatalError", exInfo);
// Also re-throw the exception if asked to
if ((throwWhen & THROW_ON_FATAL) == THROW_ON_FATAL)
{
throw new SAXException(exception);
}
}
/**
* Worker method to either log or call check* for this event.
* A simple way to validate for any kind of event.
*
* @param type of message (warning/error/fatalerror)
* @param desc description of this kind of message
* @param exInfo String representation of current exception
*/
protected void logOrCheck(int type, String desc, String exInfo)
{
String tmp = getQuickCounters() + " " + desc;
// Either log the exception or call checkPass/checkFail
// as requested by setExpected for this type
if (ITEM_DONT_CARE == expected[type])
{
// We don't care about this, just log it
logger.logMsg(level, desc + " threw: " + exInfo);
}
else if (ITEM_CHECKFAIL == expected[type])
{
// We shouldn't have been called here, so fail
logger.checkFail(desc + " threw-unexpected: " + exInfo);
}
else if (exInfo.indexOf(expected[type]) > -1)
{
// We got a warning the user expected, so pass
logger.checkPass(desc + " threw-matching: " + exInfo);
// Also reset this counter
//@todo needswork: this is very state-dependent, and
// might not be what the user expects, but at least it
// won't give lots of extra false fails or passes
expected[type] = ITEM_DONT_CARE;
}
else
{
// We got a warning the user didn't expect, so fail
logger.checkFail(desc + " threw-notmatching: " + exInfo);
// Also reset this counter
expected[type] = ITEM_DONT_CARE;
}
}
}