RESOLVED - issue BATCH-289: FieldSet should be an interface
http://jira.springframework.org/browse/BATCH-289 Created interface and moved implementation to DefaultFieldSet.
This commit is contained in:
@@ -19,8 +19,9 @@ package org.springframework.batch.io.file;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.batch.io.exception.FlatFileParsingException;
|
||||
import org.springframework.batch.io.file.mapping.FieldSet;
|
||||
import org.springframework.batch.io.file.mapping.DefaultFieldSet;
|
||||
import org.springframework.batch.io.file.mapping.FieldSetMapper;
|
||||
import org.springframework.batch.io.file.mapping.FieldSet;
|
||||
import org.springframework.batch.io.file.separator.LineReader;
|
||||
import org.springframework.batch.io.file.separator.RecordSeparatorPolicy;
|
||||
import org.springframework.batch.io.file.separator.ResourceLineReader;
|
||||
@@ -37,7 +38,7 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* This class represents a basic input source, that reads data from the file and
|
||||
* returns it as structured tuples in the form of{@link FieldSet} instances.
|
||||
* returns it as structured tuples in the form of{@link DefaultFieldSet} instances.
|
||||
* The location of the file is defined by the resource property. To separate the
|
||||
* structure of the file, {@link LineTokenizer} is used to parse data obtained
|
||||
* from the file. <br/>
|
||||
|
||||
@@ -44,15 +44,15 @@ import org.springframework.validation.ObjectError;
|
||||
|
||||
/**
|
||||
* {@link FieldSetMapper} implementation based on bean property paths. The
|
||||
* {@link FieldSet} to be mapped should have field name meta data corresponding
|
||||
* {@link DefaultFieldSet} to be mapped should have field name meta data corresponding
|
||||
* to bean property paths in a prototype instance of the desired type. The
|
||||
* prototype instance is initialized either by referring to to object by bean
|
||||
* name in the enclosing BeanFactory, or by providing a class to instantiate
|
||||
* reflectively.<br/>
|
||||
*
|
||||
* Nested property paths, including indexed properties in maps and collections,
|
||||
* can be referenced by the {@link FieldSet} names. They will be converted to
|
||||
* nested bean properties inside the prototype. The {@link FieldSet} and the
|
||||
* can be referenced by the {@link DefaultFieldSet} names. They will be converted to
|
||||
* nested bean properties inside the prototype. The {@link DefaultFieldSet} and the
|
||||
* prototype are thus tightly coupled by the fields that are available and those
|
||||
* that can be initialized. If some of the nested properties are optional (e.g.
|
||||
* collection members) they need to be removed by a post processor.<br/>
|
||||
@@ -103,7 +103,7 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
|
||||
|
||||
/**
|
||||
* The bean name (id) for an object that can be populated from the field set
|
||||
* that will be passed into {@link #mapLine(FieldSet)}. Typically a
|
||||
* that will be passed into {@link #mapLine(DefaultFieldSet)}. Typically a
|
||||
* prototype scoped bean so that a new instance is returned for each field
|
||||
* set mapped.
|
||||
*
|
||||
@@ -119,7 +119,7 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
|
||||
/**
|
||||
* Public setter for the type of bean to create instead of using a prototype
|
||||
* bean. An object of this type will be created from its default constructor
|
||||
* for every call to {@link #mapLine(FieldSet)}.<br/>
|
||||
* for every call to {@link #mapLine(DefaultFieldSet)}.<br/>
|
||||
*
|
||||
* Either this property or the prototype bean name must be specified, but
|
||||
* not both.
|
||||
@@ -144,17 +144,17 @@ public class BeanWrapperFieldSetMapper implements FieldSetMapper, BeanFactoryAwa
|
||||
}
|
||||
|
||||
/**
|
||||
* Map the {@link FieldSet} to an object retrieved from the enclosing Spring
|
||||
* Map the {@link DefaultFieldSet} to an object retrieved from the enclosing Spring
|
||||
* context, or to a new instance of the required type if no prototype is
|
||||
* available.
|
||||
*
|
||||
* @throws NotWritablePropertyException if the {@link FieldSet} contains a
|
||||
* @throws NotWritablePropertyException if the {@link DefaultFieldSet} contains a
|
||||
* field that cannot be mapped to a bean property.
|
||||
* @throws BindingException if there is a type conversion or other error (if
|
||||
* the {@link DataBinder} from {@link #createBinder(Object)} has errors
|
||||
* after binding).
|
||||
*
|
||||
* @see org.springframework.batch.io.file.mapping.FieldSetMapper#mapLine(org.springframework.batch.io.file.mapping.FieldSet)
|
||||
* @see org.springframework.batch.io.file.mapping.FieldSetMapper#mapLine(org.springframework.batch.io.file.mapping.DefaultFieldSet)
|
||||
*/
|
||||
public Object mapLine(FieldSet fs) {
|
||||
Object copy = getBean();
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.batch.io.file.mapping;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link FieldSet} using Java using Java primitive
|
||||
* and standard types and utilities. Strings are trimmed before parsing by
|
||||
* default, and so are plain String values.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class DefaultFieldSet implements FieldSet {
|
||||
|
||||
private final static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
|
||||
|
||||
/**
|
||||
* The fields wrapped by this '<code>FieldSet</code>' instance.
|
||||
*/
|
||||
private String[] tokens;
|
||||
|
||||
private List names;
|
||||
|
||||
public DefaultFieldSet(String[] tokens) {
|
||||
this.tokens = tokens == null ? null : (String[]) tokens.clone();
|
||||
}
|
||||
|
||||
public DefaultFieldSet(String[] tokens, String[] names) {
|
||||
Assert.notNull(tokens);
|
||||
Assert.notNull(names);
|
||||
if (tokens.length != names.length) {
|
||||
throw new IllegalArgumentException("Field names must be same length as values: names="
|
||||
+ Arrays.asList(names) + ", values=" + Arrays.asList(tokens));
|
||||
}
|
||||
this.tokens = (String[]) tokens.clone();
|
||||
this.names = Arrays.asList(names);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#getNames()
|
||||
*/
|
||||
public String[] getNames() {
|
||||
if (names == null) {
|
||||
throw new IllegalStateException("Field names are not known");
|
||||
}
|
||||
return (String[]) names.toArray();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#getValues()
|
||||
*/
|
||||
public String[] getValues() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readString(int)
|
||||
*/
|
||||
public String readString(int index) {
|
||||
return readAndTrim(index);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readString(java.lang.String)
|
||||
*/
|
||||
public String readString(String name) {
|
||||
return readString(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(int)
|
||||
*/
|
||||
public boolean readBoolean(int index) {
|
||||
return readBoolean(index, "true");
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(java.lang.String)
|
||||
*/
|
||||
public boolean readBoolean(String name) {
|
||||
return readBoolean(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(int,
|
||||
* java.lang.String)
|
||||
*/
|
||||
public boolean readBoolean(int index, String trueValue) {
|
||||
Assert.notNull(trueValue, "'trueValue' cannot be null.");
|
||||
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return trueValue.equals(value) ? true : false;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBoolean(java.lang.String,
|
||||
* java.lang.String)
|
||||
*/
|
||||
public boolean readBoolean(String name, String trueValue) {
|
||||
return readBoolean(indexOf(name), trueValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readChar(int)
|
||||
*/
|
||||
public char readChar(int index) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
Assert.isTrue(value.length() == 1, "Cannot convert field value '" + value + "' to char.");
|
||||
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readChar(java.lang.String)
|
||||
*/
|
||||
public char readChar(String name) {
|
||||
return readChar(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readByte(int)
|
||||
*/
|
||||
public byte readByte(int index) {
|
||||
return Byte.parseByte(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readByte(java.lang.String)
|
||||
*/
|
||||
public byte readByte(String name) {
|
||||
return readByte(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readShort(int)
|
||||
*/
|
||||
public short readShort(int index) {
|
||||
return Short.parseShort(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readShort(java.lang.String)
|
||||
*/
|
||||
public short readShort(String name) {
|
||||
return readShort(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(int)
|
||||
*/
|
||||
public int readInt(int index) {
|
||||
return Integer.parseInt(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(java.lang.String)
|
||||
*/
|
||||
public int readInt(String name) {
|
||||
return readInt(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(int,
|
||||
* int)
|
||||
*/
|
||||
public int readInt(int index, int defaultValue) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return StringUtils.hasLength(value) ? Integer.parseInt(value) : defaultValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readInt(java.lang.String,
|
||||
* int)
|
||||
*/
|
||||
public int readInt(String name, int defaultValue) {
|
||||
return readInt(indexOf(name), defaultValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(int)
|
||||
*/
|
||||
public long readLong(int index) {
|
||||
return Long.parseLong(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(java.lang.String)
|
||||
*/
|
||||
public long readLong(String name) {
|
||||
return readLong(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(int,
|
||||
* long)
|
||||
*/
|
||||
public long readLong(int index, long defaultValue) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return StringUtils.hasLength(value) ? Long.parseLong(value) : defaultValue;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readLong(java.lang.String,
|
||||
* long)
|
||||
*/
|
||||
public long readLong(String name, long defaultValue) {
|
||||
return readLong(indexOf(name), defaultValue);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readFloat(int)
|
||||
*/
|
||||
public float readFloat(int index) {
|
||||
return Float.parseFloat(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readFloat(java.lang.String)
|
||||
*/
|
||||
public float readFloat(String name) {
|
||||
return readFloat(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDouble(int)
|
||||
*/
|
||||
public double readDouble(int index) {
|
||||
return Double.parseDouble(readAndTrim(index));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDouble(java.lang.String)
|
||||
*/
|
||||
public double readDouble(String name) {
|
||||
return readDouble(indexOf(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(int)
|
||||
*/
|
||||
public BigDecimal readBigDecimal(int index) {
|
||||
return readBigDecimal(index, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(java.lang.String)
|
||||
*/
|
||||
public BigDecimal readBigDecimal(String name) {
|
||||
return readBigDecimal(name, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(int,
|
||||
* java.math.BigDecimal)
|
||||
*/
|
||||
public BigDecimal readBigDecimal(int index, BigDecimal defaultValue) {
|
||||
String candidate = readAndTrim(index);
|
||||
|
||||
try {
|
||||
return (StringUtils.hasText(candidate)) ? new BigDecimal(candidate) : defaultValue;
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Unparseable number: " + candidate);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readBigDecimal(java.lang.String,
|
||||
* java.math.BigDecimal)
|
||||
*/
|
||||
public BigDecimal readBigDecimal(String name, BigDecimal defaultValue) {
|
||||
try {
|
||||
return readBigDecimal(indexOf(name), defaultValue);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", name: [" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(int)
|
||||
*/
|
||||
public Date readDate(int index) {
|
||||
return readDate(index, DEFAULT_DATE_PATTERN);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(java.lang.String)
|
||||
*/
|
||||
public Date readDate(String name) {
|
||||
return readDate(name, DEFAULT_DATE_PATTERN);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(int,
|
||||
* java.lang.String)
|
||||
*/
|
||||
public Date readDate(int index, String pattern) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||
Date date;
|
||||
String value = readAndTrim(index);
|
||||
try {
|
||||
date = sdf.parse(value);
|
||||
}
|
||||
catch (ParseException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", pattern: [" + pattern + "]");
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#readDate(java.lang.String,
|
||||
* java.lang.String)
|
||||
*/
|
||||
public Date readDate(String name, String pattern) {
|
||||
try {
|
||||
return readDate(indexOf(name), pattern);
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", name: [" + name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#getFieldCount()
|
||||
*/
|
||||
public int getFieldCount() {
|
||||
return tokens.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and trim the {@link String} value at '<code>index</code>'.
|
||||
*
|
||||
* @throws NullPointerException if the field value is <code>null</code>.
|
||||
*/
|
||||
protected String readAndTrim(int index) {
|
||||
String value = tokens[index];
|
||||
|
||||
if (value != null) {
|
||||
return value.trim();
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and trim the {@link String} value from column with given '<code>name</code>.
|
||||
*
|
||||
* @throws IllegalArgumentException if a column with given name is not
|
||||
* defined.
|
||||
*/
|
||||
protected int indexOf(String name) {
|
||||
if (names == null) {
|
||||
throw new IllegalArgumentException("Cannot access columns by name without meta data");
|
||||
}
|
||||
int index = names.indexOf(name);
|
||||
if (index >= 0) {
|
||||
return index;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot access column [" + name + "] from " + names);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (names != null) {
|
||||
return getProperties().toString();
|
||||
}
|
||||
|
||||
return tokens == null ? "" : Arrays.asList(tokens).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof DefaultFieldSet) {
|
||||
DefaultFieldSet fs = (DefaultFieldSet) object;
|
||||
|
||||
if (this.tokens == null) {
|
||||
return fs.tokens == null;
|
||||
}
|
||||
else {
|
||||
return Arrays.equals(this.tokens, fs.tokens);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
// this algorithm was taken from java 1.5 jdk Arrays.hashCode(Object[])
|
||||
if (tokens == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 1;
|
||||
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
result = 31 * result + (tokens[i] == null ? 0 : tokens[i].hashCode());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.batch.io.file.mapping.IFieldSet#getProperties()
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
if (names == null) {
|
||||
throw new IllegalStateException("Cannot create properties without meta data");
|
||||
}
|
||||
Properties props = new Properties();
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String value = readAndTrim(i);
|
||||
if (value != null) {
|
||||
props.setProperty((String) names.get(i), value);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,652 +0,0 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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.springframework.batch.io.file.mapping;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Rob Harrop
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class FieldSet {
|
||||
|
||||
private final static String DEFAULT_DATE_PATTERN = "yyyy-MM-dd";
|
||||
|
||||
/**
|
||||
* The fields wrapped by this '<code>FieldSet</code>' instance.
|
||||
*/
|
||||
private String[] tokens;
|
||||
|
||||
private List names;
|
||||
|
||||
public FieldSet(String[] tokens) {
|
||||
this.tokens = tokens == null ? null : (String[]) tokens.clone();
|
||||
}
|
||||
|
||||
public FieldSet(String[] tokens, String[] names) {
|
||||
Assert.notNull(tokens);
|
||||
Assert.notNull(names);
|
||||
if (tokens.length != names.length) {
|
||||
throw new IllegalArgumentException(
|
||||
"Field names must be same length as values: names="
|
||||
+ Arrays.asList(names) + ", values="
|
||||
+ Arrays.asList(tokens));
|
||||
}
|
||||
this.tokens = (String[]) tokens.clone();
|
||||
this.names = Arrays.asList(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public accessor for the names property.
|
||||
*
|
||||
* @return the names
|
||||
*
|
||||
* @throws IllegalStateException if the names are not defined
|
||||
*/
|
||||
public String[] getNames() {
|
||||
if (names == null) {
|
||||
throw new IllegalStateException(
|
||||
"Field names are not known");
|
||||
}
|
||||
return (String[]) names.toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return fields wrapped by this '<code>FieldSet</code>' instance as String values.
|
||||
*/
|
||||
public String[] getValues() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link String} value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public String readString(int index) {
|
||||
return readAndTrim(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link String} value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
*/
|
||||
public String readString(String name) {
|
||||
return readString(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>boolean</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public boolean readBoolean(int index) {
|
||||
return readBoolean(index, "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>boolean</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public boolean readBoolean(String name) {
|
||||
return readBoolean(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>boolean</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @param trueValue
|
||||
* the value that signifies {@link Boolean#TRUE true};
|
||||
* case-sensitive.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds, or if the supplied
|
||||
* <code>trueValue</code> is <code>null</code>.
|
||||
*/
|
||||
public boolean readBoolean(int index, String trueValue) {
|
||||
Assert.notNull(trueValue, "'trueValue' cannot be null.");
|
||||
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return trueValue.equals(value) ? true : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>boolean</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @param trueValue
|
||||
* the value that signifies {@link Boolean#TRUE true};
|
||||
* case-sensitive.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined, or if the
|
||||
* supplied <code>trueValue</code> is <code>null</code>.
|
||||
*/
|
||||
public boolean readBoolean(String name, String trueValue) {
|
||||
return readBoolean(indexOf(name), trueValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>char</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public char readChar(int index) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
Assert.isTrue(value.length() == 1, "Cannot convert field value '"
|
||||
+ value + "' to char.");
|
||||
|
||||
return value.charAt(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>char</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public char readChar(String name) {
|
||||
return readChar(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>byte</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public byte readByte(int index) {
|
||||
return Byte.parseByte(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>byte</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
*/
|
||||
public byte readByte(String name) {
|
||||
return readByte(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>short</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public short readShort(int index) {
|
||||
return Short.parseShort(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>short</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public short readShort(String name) {
|
||||
return readShort(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>int</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public int readInt(int index) {
|
||||
return Integer.parseInt(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>int</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public int readInt(String name) {
|
||||
return readInt(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>int</code>' value at index '<code>index</code>',
|
||||
* using the supplied <code>defaultValue</code> if the field value is
|
||||
* blank.
|
||||
*
|
||||
* @param index
|
||||
* the field index..
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public int readInt(int index, int defaultValue) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return StringUtils.hasLength(value) ? Integer.parseInt(value)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>int</code>' value from column with given '<code>name</code>',
|
||||
* using the supplied <code>defaultValue</code> if the field value is
|
||||
* blank.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public int readInt(String name, int defaultValue) {
|
||||
return readInt(indexOf(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>long</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public long readLong(int index) {
|
||||
return Long.parseLong(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>long</code>' value from column with given '<code>name</code>'.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public long readLong(String name) {
|
||||
return readLong(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>long</code>' value at index '<code>index</code>',
|
||||
* using the supplied <code>defaultValue</code> if the field value is
|
||||
* blank.
|
||||
*
|
||||
* @param index
|
||||
* the field index..
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public long readLong(int index, long defaultValue) {
|
||||
String value = readAndTrim(index);
|
||||
|
||||
return StringUtils.hasLength(value) ? Long.parseLong(value)
|
||||
: defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>long</code>' value from column with given '<code>name</code>',
|
||||
* using the supplied <code>defaultValue</code> if the field value is
|
||||
* blank.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public long readLong(String name, long defaultValue) {
|
||||
return readLong(indexOf(name), defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>float</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public float readFloat(int index) {
|
||||
return Float.parseFloat(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>float</code>' value from column with given '<code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public float readFloat(String name) {
|
||||
return readFloat(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>double</code>' value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public double readDouble(int index) {
|
||||
return Double.parseDouble(readAndTrim(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the '<code>double</code>' value from column with given '<code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public double readDouble(String name) {
|
||||
return readDouble(indexOf(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link java.math.BigDecimal} value at index '<code>index</code>'.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public BigDecimal readBigDecimal(int index) {
|
||||
return readBigDecimal(index, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link java.math.BigDecimal} value from column with given '<code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public BigDecimal readBigDecimal(String name) {
|
||||
return readBigDecimal(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link BigDecimal} value at index '<code>index</code>',
|
||||
* returning the supplied <code>defaultValue</code> if the trimmed string
|
||||
* value at index '<code>index</code>' is blank.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
*/
|
||||
public BigDecimal readBigDecimal(int index, BigDecimal defaultValue) {
|
||||
String candidate = readAndTrim(index);
|
||||
|
||||
try {
|
||||
return (StringUtils.hasText(candidate)) ? new BigDecimal(candidate)
|
||||
: defaultValue;
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Unparseable number: "
|
||||
+ candidate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the {@link BigDecimal} value from column with given '<code>name</code>,
|
||||
* returning the supplied <code>defaultValue</code> if the trimmed string
|
||||
* value at index '<code>index</code>' is blank.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
public BigDecimal readBigDecimal(String name, BigDecimal defaultValue) {
|
||||
try {
|
||||
return readBigDecimal(indexOf(name), defaultValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", name: ["
|
||||
+ name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the <code>java.util.Date</code> value in default format at
|
||||
* designated column <code>index</code>.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @param pattern
|
||||
* the pattern describing the date and time format
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
* @see #DEFAULT_DATE_PATTERN
|
||||
*/
|
||||
public Date readDate(int index) {
|
||||
return readDate(index, DEFAULT_DATE_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the <code>java.sql.Date</code> value in given format from column
|
||||
* with given <code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @param pattern
|
||||
* the pattern describing the date and time format
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
* @see #DEFAULT_DATE_PATTERN
|
||||
*/
|
||||
public Date readDate(String name) {
|
||||
return readDate(name, DEFAULT_DATE_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the <code>java.util.Date</code> value in default format at
|
||||
* designated column <code>index</code>.
|
||||
*
|
||||
* @param index
|
||||
* the field index.
|
||||
* @param pattern
|
||||
* the pattern describing the date and time format
|
||||
* @throws IndexOutOfBoundsException
|
||||
* if the index is out of bounds.
|
||||
* @throws IllegalArgumentException
|
||||
* if the date cannot be parsed.
|
||||
*
|
||||
*/
|
||||
public Date readDate(int index, String pattern) {
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
|
||||
Date date;
|
||||
String value = readAndTrim(index);
|
||||
try {
|
||||
date = sdf.parse(value);
|
||||
} catch (ParseException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", pattern: ["
|
||||
+ pattern + "]");
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the <code>java.sql.Date</code> value in given format from column
|
||||
* with given <code>name</code>.
|
||||
*
|
||||
* @param name
|
||||
* the field name.
|
||||
* @param pattern
|
||||
* the pattern describing the date and time format
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined or if the
|
||||
* specified field cannot be parsed
|
||||
*
|
||||
*/
|
||||
public Date readDate(String name, String pattern) {
|
||||
try {
|
||||
return readDate(indexOf(name), pattern);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new IllegalArgumentException(e.getMessage() + ", name: ["
|
||||
+ name + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of fields in this '<code>FieldSet</code>'.
|
||||
*/
|
||||
public int getFieldCount() {
|
||||
return tokens.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and trim the {@link String} value at '<code>index</code>'.
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if the field value is <code>null</code>.
|
||||
*/
|
||||
private String readAndTrim(int index) {
|
||||
String value = tokens[index];
|
||||
|
||||
if (value != null) {
|
||||
return value.trim();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and trim the {@link String} value from column with given '<code>name</code>.
|
||||
*
|
||||
* @throws IllegalArgumentException
|
||||
* if a column with given name is not defined.
|
||||
*/
|
||||
private int indexOf(String name) {
|
||||
if (names == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot access columns by name without meta data");
|
||||
}
|
||||
int index = names.indexOf(name);
|
||||
if (index >= 0) {
|
||||
return index;
|
||||
}
|
||||
throw new IllegalArgumentException("Cannot access column [" + name
|
||||
+ "] from " + names);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (names != null) {
|
||||
return getProperties().toString();
|
||||
}
|
||||
|
||||
return tokens == null ? "" : Arrays.asList(tokens).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof FieldSet) {
|
||||
FieldSet fs = (FieldSet) object;
|
||||
|
||||
if (this.tokens == null) {
|
||||
return fs.tokens == null;
|
||||
} else {
|
||||
return Arrays.equals(this.tokens, fs.tokens);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
// this algorithm was taken from java 1.5 jdk Arrays.hashCode(Object[])
|
||||
if (tokens == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 1;
|
||||
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
result = 31 * result
|
||||
+ (tokens[i] == null ? 0 : tokens[i].hashCode());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct name-value pairs from the field names and string values. Null
|
||||
* values are omitted.
|
||||
*
|
||||
* @return some properties representing the field set.
|
||||
*
|
||||
* @throws IllegalStateException
|
||||
* if the field name meta data is not available.
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
if (names == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot create properties without meta data");
|
||||
}
|
||||
Properties props = new Properties();
|
||||
for (int i = 0; i < tokens.length; i++) {
|
||||
String value = readAndTrim(i);
|
||||
if (value != null) {
|
||||
props.setProperty((String) names.get(i), value);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ package org.springframework.batch.io.file.transform;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.batch.io.file.mapping.DefaultFieldSet;
|
||||
import org.springframework.batch.io.file.mapping.FieldSet;
|
||||
|
||||
|
||||
@@ -63,7 +64,7 @@ public abstract class AbstractLineTokenizer implements LineTokenizer {
|
||||
public FieldSet tokenize(String line) {
|
||||
|
||||
if (line == null || line.length()==0) {
|
||||
return new FieldSet(new String[0]);
|
||||
return new DefaultFieldSet(new String[0]);
|
||||
}
|
||||
|
||||
List tokens = new ArrayList(doTokenize(line));
|
||||
@@ -73,9 +74,9 @@ public abstract class AbstractLineTokenizer implements LineTokenizer {
|
||||
|
||||
String[] values = (String[]) tokens.toArray(new String[tokens.size()]);
|
||||
if (names.length==0) {
|
||||
return new FieldSet(values);
|
||||
return new DefaultFieldSet(values);
|
||||
}
|
||||
return new FieldSet(values, names);
|
||||
return new DefaultFieldSet(values, names);
|
||||
}
|
||||
|
||||
protected abstract List doTokenize(String line);
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.batch.io.file.mapping.DefaultFieldSet;
|
||||
import org.springframework.batch.io.file.mapping.FieldSet;
|
||||
|
||||
public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer {
|
||||
@@ -34,7 +35,7 @@ public class PrefixMatchingCompositeLineTokenizer implements LineTokenizer {
|
||||
public FieldSet tokenize(String line) {
|
||||
|
||||
if (line==null) {
|
||||
return new FieldSet(new String[0]);
|
||||
return new DefaultFieldSet(new String[0]);
|
||||
}
|
||||
|
||||
LineTokenizer tokenizer = null;
|
||||
|
||||
Reference in New Issue
Block a user