/* Copyright (c) 2005 Ricebridge. All Rights Reserved. * * This file is available under the terms and conditions of the * Ricebridge "Open Source API" policy; Ricebridge grants use of this * copyrighted work under the terms of a BSD-style license only. See * http://www.opensource.org/licenses/bsd-license.php for more * information. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * - Neither the name of the Ricebridge nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.ricebridge.xmlman; import org.jostraca.util.Internal; import java.util.*; /** Describes a bad data record that failed for some reason. * <p>Data records fail when the {@link RecordListener} processing them returns a <code>BadRecord</code> * or throws an <code>Exception</code>. * Either the actual data in the data fields was incorrect in some way, or the processing of the data record * in a custom <code>RecordListener</code> failed due to database errors or similar problems.</p> * <p>Since XML does not allow recovery from syntax errors, a <code>BadRecord</code> does not describe an XML syntax error, * and no <code>BadRecord</code> object is created when an XML syntax error occurs. Instead, * an {@link XmlManagerException} is always thrown, and processing halts. Note that the {@link RecordListener#endProcess endProcess} * methods of any <code>RecordListeners</code> or <code>RecordProviders</code> * are still called when this happens, so that you can exit cleanly.</p> * <p>When a {@link RecordListener} encounters a problem with a data record, it can choose to return a * <code>BadRecord</code> object to <i>XML Manager</i> from the {@link RecordListener#handleRecord handleRecord} method. * This <code>BadRecord</code> is stored for later examination * and you can access it using the {@link XmlManager#getBadRecords} method. Normally, only one <code>BadRecord</code> is available, * as the default value for the setting {@link XmlSpec#setIgnoreBadRecords XmlSpec.setIgnoreBadRecords} is <code>false</code>. * If you set this setting to <code>true</code>, then processing will continue despite errors in * {@link RecordListener RecordListeners}, and you can then examine all <code>BadRecords</code> that occurred * using the <code>getBadRecords</code> method.</p> * <p>When an error occurs in a custom {@link RecordListener} that you have written, you have two options; * 1. you can create a <code>BadRecord</code> object yourself, with all the details you consider relevant, and return it, * or 2. you can just throw an <code>Exception</code>, and let XML Manager create a default BadRecord for you. * When option for option 1, you would normally create a BadRecord of {@link #SEMANTIC} type, using the * convenience constructor {@link #BadRecord(long,String[],Exception)} * (The <code>pRecordNumber</code> and <code>pRecord</code> parameters can be obtained from the * arguments to the <code>handleRecord</code> method). * For option 2, it is much easier (and recommended) to use the support classes such as {@link RecordListenerSupport}, as these * handle checked <code>Exceptions</code> automatically.</p> * <p>You can also subclass <code>BadRecord</code> to provide additional functionality for your own applications.</p> * <p>The <b><a href="BadRecord.java.html">Source Code</a></b> of this Java class * is available under a <a href="http://www.opensource.org/licenses/bsd-license.php">BSD-style license</a>.</p> */ public class BadRecord { // public static /** Indicate that the bad record has an unknown origin. */ public static final int UNKNOWN = 0; /** Indicate that the bad record was returned by the {@link RecordListener} (usually in response to invalid data). */ public static final int SEMANTIC = 1; /** Indicate that the bad record occurred because an <code>Exception</code> was thrown in the {@link RecordListener}. */ public static final int UNEXPECTED = 2; // protected static /** String names for the type of <code>BadRecord</code>. */ protected static final String[] sTypeNames = new String[] { "UNKNOWN", "SEMANTIC", "UNEXPECTED" }; // protected instance /** An identification tag for the <code>BadRecord</code> origin, usually the record XPath expression, if available. */ protected String iTag; /** The number of the record that caused the error (this number is specific to each {@link RecordSpec}). */ protected long iRecordNumber; /** The data record that caused the error, if available. */ protected String[] iRecord; /** A technical error message. */ protected String iMessage; /** A context object describing the error, usually the <code>Exception</code> that caused it. */ protected Object iContext; /** The type of error that created the BadRecord; {@link #UNKNOWN}, {@link #SEMANTIC}, {@link #UNEXPECTED}. */ protected int iType; // constructors /** Create an empty <code>BadRecord</code> with no information content. */ public BadRecord() { this("",0,new String[]{},"",null); } /** Create a <code>BadRecord</code> based on an Exception. * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pException Exception that caused the error */ public BadRecord( long pRecordNumber, String[] pRecord, Exception pException ) { this( "", pRecordNumber, pRecord, pException.getMessage(), pException ); } /** Create a <code>BadRecord</code> with a specific error message. * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pMessage technical message describing the error */ public BadRecord( long pRecordNumber, String[] pRecord, String pMessage ) { this( "", pRecordNumber, pRecord, pMessage, SEMANTIC ); } /** Create a <code>BadRecord</code> with a specific error message and * indicate the record XPath using the <code>pTag</code> parameter. * @param pTag indentify the origin of the BadRecord, usually the XPath record expression * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pMessage technical message describing the error */ public BadRecord( String pTag, long pRecordNumber, String[] pRecord, String pMessage ) { this( pTag, pRecordNumber, pRecord, pMessage, SEMANTIC ); } /** Create a <code>BadRecord</code> with a specific error message and type, * and indicate the record XPath using the <code>pTag</code> parameter. * @param pTag indentify the origin of the BadRecord, usually the XPath record expression * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pMessage technical message describing the error * @param pType type of <code>BadRecord</code>: {@link #SEMANTIC}, {@link #UNEXPECTED}, {@link #UNKNOWN} */ public BadRecord( String pTag, long pRecordNumber, String[] pRecord, String pMessage, int pType ) { iTag = Internal.null_arg( pTag ); iRecordNumber = pRecordNumber; iRecord = (String[]) Internal.null_array( pRecord ); iMessage = Internal.null_arg( pMessage ); iType = pType; } /** Create a <code>BadRecord</code> with a specific error message and context, and * indicate the record XPath using the <code>pTag</code> parameter. * @param pTag indentify the origin of the BadRecord, usually the XPath record expression * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pMessage technical message describing the error * @param pContext context of the error, usually an Exception, can be null */ public BadRecord( String pTag, long pRecordNumber, String[] pRecord, String pMessage, Object pContext ) { this( pTag, pRecordNumber, pRecord, pMessage, pContext, SEMANTIC ); } /** Create a fully described <code>BadRecord</code> object. * @param pTag indentify the origin of the BadRecord, usually the XPath record expression * @param pRecordNumber number of the record that caused the error (counted per {@link RecordSpec}) * @param pRecord record data (may be partial) * @param pMessage technical message describing the error * @param pContext context of the error, usually an Exception, can be null * @param pType type of <code>BadRecord</code>: {@link #SEMANTIC}, {@link #UNEXPECTED}, {@link #UNKNOWN} */ public BadRecord( String pTag, long pRecordNumber, String[] pRecord, String pMessage, Object pContext, int pType ) { this( pTag, pRecordNumber, pRecord, pMessage, pType ); iContext = pContext; } // public methods /** Get the origin identification tag, usually the record XPath expression. */ public String getTag() { return iTag; } /** Get the record number of the failed record. * <p>The record number is a counter specific to each {@link RecordSpec}. * If you are using more than one <code>RecordSpec</code> and you need a global counter, you will need to implement one in * a custom {@link RecordListener}.</p> */ public long getRecordNumber() { return iRecordNumber; } /** Get the record data fields of the record that failed. * <p>Note: this <code>String[]</code> array may only contain partial data and * may also contain <code>nulls</code>.</p> */ public String[] getRecord() { return iRecord; } /** Get the technical error message associated with this failed record. * <p>This message is intended for debugging purposes and is not normally * intended for users.</p> */ public String getMessage() { return iMessage; } /** Get the context in which the data record failed. * <p>This can be any object that describes the error condition, but is normally the * <code>Exception</code> that caused the processing of this data record to fail.</p> */ public Object getContext() { return iContext; } /** Get the type of bad record that occurred. * <p>The current error types are <code>SEMANTIC</code>, for * invalid data values, and <code>UNEXPECTED</code>, for unexpected exceptions thrown by the * {@link RecordListener} (correctly functioning <code>RecordListener</code>s are * expected to return a <code>BadRecord</code> object instead of throwing an <code>Exception</code>).</p> */ public int getType() { return iType; } /** Return a textual description of the <code>BadRecord</code> suitable for debugging purposes. */ public String toString() { StringBuffer sb = new StringBuffer("[BadRecord:"); int type = getType(); sb.append( sTypeNames[ (0 < type && type < sTypeNames.length) ? type : 0 ] ); sb.append( ",tag="+getTag() ); sb.append( ",recnum="+getRecordNumber() ); sb.append( ",fields=" ); String[] rec = getRecord(); if( null != rec ) { sb.append( Arrays.asList(getRecord()) ); } else { sb.append( "[unknown]" ); } sb.append( ",msg="+getMessage() ); sb.append( ",context="+getContext() ); sb.append("]"); return sb.toString(); } }