
org.beanio.parser.Record Maven / Gradle / Ivy
/*
* Copyright 2010-2011 Kevin Seim
*
* Licensed 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.
*/
package org.beanio.parser;
import java.text.MessageFormat;
import java.util.*;
import org.beanio.BeanReaderContext;
import org.beanio.stream.RecordReader;
/**
* A Record holds state information about the last record read from an
* input stream. A single Record instance is used repeatedly while
* reading the input stream. Before each record is read, clear() is
* called to clear the state of the last record.
*
* @author Kevin Seim
* @since 1.0
*/
public class Record {
/* the line number of the last record read from the stream */
private int lineNumber;
/* the raw record text of the last record read from the stream */
private String recordText;
/* the name of the record last read from the stream, if known */
private String recordName;
/* stores the current field offset */
private LinkedList fieldOffsets = new LinkedList();
private Map fieldMap = new HashMap();
private Map> collectionFieldMap = new HashMap>();
private Map> fieldErrors;
private Collection recordErrors;
private Locale locale;
private MessageFactory messageContext;
/**
* Constructs a new Record.
*/
public Record() { }
/**
* Clears the current state of the record before the next record is read.
*/
public void clear() {
this.recordText = null;
this.recordName = null;
this.fieldMap.clear();
this.collectionFieldMap.clear();
this.fieldErrors = null;
this.recordErrors = null;
this.fieldOffsets.clear();
}
/**
* Sets the value of the record as returned from the RecordReader
* @param value the record value read by a record reader
* @see RecordReader
*/
public void setValue(Object value) { }
/**
* Returns the current state of the bean reader stored by this class.
* @return the current state of the reader
*/
public BeanReaderContext getContext() {
BeanReaderContextImpl ctx = new BeanReaderContextImpl();
ctx.lineNumber = lineNumber;
ctx.recordText = recordText;
ctx.recordName = recordName;
ctx.recordErrors = recordErrors;
ctx.fieldErrorMap = fieldErrors;
// the same field maps are used over and over by this implementation, so a copy is made for the context
if (fieldMap != null && !fieldMap.isEmpty()) {
ctx.fieldTextMap = new HashMap(fieldMap);
}
if (collectionFieldMap != null && !collectionFieldMap.isEmpty()) {
ctx.collectionFieldTextMap = new HashMap>(collectionFieldMap);
}
return ctx;
}
/**
* Returns true if a field error was reported while parsing
* this record.
* @return true if a field error was reported
*/
public boolean hasFieldErrors() {
return fieldErrors != null && !fieldErrors.isEmpty();
}
/**
* Returns true if a field error was reported for a specific field.
* @param fieldName the name of the field to check
* @return true if an error was reported for the field
*/
public boolean hasFieldErrors(String fieldName) {
if (fieldErrors == null)
return false;
Collection errors = fieldErrors.get(fieldName);
return errors != null && !errors.isEmpty();
}
/**
* Returns true if a record level error was reported while parsing
* this record.
* @return true if a record level error was reported
*/
public boolean hasRecordErrors() {
return recordErrors != null && !recordErrors.isEmpty();
}
/**
* Adds a field error to this record.
* @param fieldName the name of the field in error
* @param fieldText the invalid field text
* @param rule the name of the failed validation rule
* @param params an optional list of parameters for formatting the error message
* @return the formatted field error message
*/
public String addFieldError(String fieldName, String fieldText, String rule, Object... params) {
String recordLabel = messageContext.getRecordLabel(recordName);
String fieldLabel = messageContext.getFieldLabel(recordName, fieldName);
if (recordLabel == null)
recordLabel = "'" + recordName + "'";
if (fieldLabel == null)
fieldLabel = "'" + fieldName + "'";
Object[] messageParams;
if (params.length == 0) {
messageParams = new Object[] { lineNumber, recordLabel, fieldLabel, fieldText };
}
else {
messageParams = new Object[4 + params.length];
messageParams[0] = lineNumber;
messageParams[1] = recordLabel;
messageParams[2] = fieldLabel;
messageParams[3] = fieldText;
System.arraycopy(params, 0, messageParams, 4, params.length);
}
String pattern = messageContext.getFieldErrorMessage(recordName, fieldName, rule);
MessageFormat mf = new MessageFormat(pattern, locale);
String message = mf.format(messageParams);
addFieldErrorMessage(fieldName, message);
return message;
}
/**
* Adds a record level error to this record.
* @param rule the name of the failed validation rule
* @param params an optional list of parameters for formatting the error message
* @return the formatted record error message
*/
public String addRecordError(String rule, Object... params) {
String recordLabel = messageContext.getRecordLabel(recordName);
if (recordLabel == null) {
recordLabel = "'" + recordName + "'";
}
Object[] messageParams;
if (params.length == 0) {
messageParams = new Object[] { lineNumber, recordLabel, recordText };
}
else {
messageParams = new Object[3 + params.length];
messageParams[0] = lineNumber;
messageParams[1] = recordLabel;
messageParams[2] = recordText;
System.arraycopy(params, 0, messageParams, 3, params.length);
}
String pattern = messageContext.getRecordErrorMessage(recordName, rule);
MessageFormat mf = new MessageFormat(pattern, locale);
String message = mf.format(messageParams);
addRecordErrorMessage(message);
return message;
}
/**
* Adds a field error message.
* @param fieldName the name of the field
* @param message the error message to add
*/
protected void addFieldErrorMessage(String fieldName, String message) {
if (fieldErrors == null) {
fieldErrors = new HashMap>();
}
Collection errors = fieldErrors.get(fieldName);
if (errors == null) {
errors = new ArrayList();
errors.add(message);
fieldErrors.put(fieldName, errors);
}
else {
errors.add(message);
}
}
/**
* Adds a record level error message.
* @param message the error message to add
*/
protected void addRecordErrorMessage(String message) {
if (recordErrors == null) {
recordErrors = new ArrayList(3);
}
recordErrors.add(message);
}
/**
* Returns the raw text of the last record read from the record reader.
* @return the raw text of the last record read
*/
public String getRecordText() {
return recordText;
}
/**
* Sets the raw text of the last record read from the record reader.
* @param text the raw text of the last record read
*/
public void setRecordText(String text) {
this.recordText = text;
}
/**
* Returns the starting line number of the last record read from the record reader.
* @return the line number of the last record
*/
public int getRecordLineNumber() {
return lineNumber;
}
/**
* Sets the starting line number of the last record read from the record reader.
* @param lineNumber the line number of the last record
*/
public void setLineNumber(int lineNumber) {
if (lineNumber > 0)
this.lineNumber = lineNumber;
}
/**
* Returns the name of the last record read from the record reader,
* or null if not known.
* @return the name of the record
*/
public String getRecordName() {
return recordName;
}
/**
* Sets the name of the last record read from the record reader.
* @param recordName
*/
public void setRecordName(String recordName) {
this.recordName = recordName;
}
/**
* Returns the locale used to format error messages.
* @return the locale used to format erorr messages
*/
public Locale getLocale() {
return locale;
}
/**
* Sets the locale used to format error messages.
* @param locale
*/
public void setLocale(Locale locale) {
this.locale = locale;
}
/**
* Sets the MessageContext to use to format erorr messages.
* @param messageContext the MessageContext to use to format
* erorr messages
*/
public void setMessageContext(MessageFactory messageContext) {
this.messageContext = messageContext;
}
/**
* Sets the raw field text for a named field.
* @param fieldName the name of the field
* @param text the raw field text
*/
public void setFieldText(String fieldName, String text) {
int fieldIndex = getFieldOffset();
if (fieldIndex == 0) {
fieldMap.put(fieldName, text);
}
else {
List list = collectionFieldMap.get(fieldName);
if (list == null) {
list = new ArrayList();
collectionFieldMap.put(fieldName, list);
}
int index = fieldIndex - 1;
if (index < list.size()) {
list.set(fieldIndex - 1, text);
}
else {
while (index > list.size()) {
list.add(null);
}
list.add(text);
}
}
}
/**
* Returns the unparsed text of a field from this record (if set).
* If the field is a collection, this method returns the field text for
* the first occurrence of the field.
* @param fieldName the name of the field to get the text for
* @return the unparsed field text
* @see #setFieldText(String, String)
*/
public String getFieldText(String fieldName) {
return getFieldText(fieldName, 0);
}
/**
* Returns the unparsed text of a field from this record (if set).
* @param fieldName the name of the field to get the text for
* @param index the index of the field, beginning at 0, for collection type
* fields
* @return the unparsed field text
* @see #setFieldText(String, String)
*/
public String getFieldText(String fieldName, int index) {
if (index == 0) {
return fieldMap.get(fieldName);
}
else {
List list = collectionFieldMap.get(fieldName);
if (list == null) {
return null;
}
index = index - 1;
if (index < list.size()) {
return list.get(index);
}
else {
return null;
}
}
}
/**
* Pushes a field onto the stack.
*/
public void pushField() {
fieldOffsets.addFirst(0);
}
/**
* Sets the current index of the collection being parsed, from which
* all subsequent parsing will be offset.
* @param index the current collection index
*/
public void setFieldOffset(int index) {
fieldOffsets.set(0, index);
}
/**
* Returns the index (or offset) of the current collection being parsed.
* @return the index of the current collection being parsed
*/
public int getFieldOffset() {
if (fieldOffsets.isEmpty())
return 0;
else
return fieldOffsets.getFirst();
}
/**
* Pops the last field from the stack.
*/
public void popField() {
fieldOffsets.removeFirst();
}
/**
* Returns the stack of field offsets. The most recently parsed field is
* at the beginning of the list.
* @return the field offset stack
*/
public List getFieldOffsets() {
return fieldOffsets;
}
@Override
public String toString() {
String name = recordName == null ? "?" : recordName;
return "Record '" + name + "' at line " + lineNumber +
" {" + recordText + "}";
}
/*
* BeanReaderContext implementation.
*/
private static class BeanReaderContextImpl implements BeanReaderContext {
int lineNumber;
String recordText;
String recordName;
Collection recordErrors;
Map fieldTextMap;
Map> collectionFieldTextMap;
Map> fieldErrorMap;
public int getRecordLineNumber() {
return lineNumber;
}
public String getRecordText() {
return recordText;
}
public String getRecordName() {
return recordName;
}
public boolean hasRecordErrors() {
return recordErrors != null && !recordErrors.isEmpty();
}
public Collection getRecordErrors() {
if (recordErrors == null)
recordErrors = Collections.emptyList();
return recordErrors;
}
public String getFieldText(String fieldName) {
if (fieldTextMap == null)
return null;
else
return fieldTextMap.get(fieldName);
}
public String getFieldText(String fieldName, int index) {
if (fieldTextMap == null) {
return null;
}
else if (index == 0) {
return fieldTextMap.get(fieldName);
}
else {
List list = collectionFieldTextMap.get(fieldName);
if (list == null) {
return null;
}
index = index - 1;
if (index < list.size()) {
return list.get(index);
}
else {
return null;
}
}
}
public boolean hasFieldErrors() {
return fieldErrorMap != null && !fieldErrorMap.isEmpty();
}
public Map> getFieldErrors() {
if (fieldErrorMap == null)
fieldErrorMap = Collections.emptyMap();
return fieldErrorMap;
}
public Collection getFieldErrors(String fieldName) {
if (fieldErrorMap == null)
return null;
else
return fieldErrorMap.get(fieldName);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy