Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * 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.item.file.mapping;
 
 import java.util.Map;
 import java.util.Set;
 
FieldSetMapper implementation based on bean property paths. The org.springframework.batch.item.file.transform.FieldSet to be mapped should have field name meta data corresponding to bean property paths in an instance of the desired type. The instance is created and initialized either by referring to to a prototype object by bean name in the enclosing BeanFactory, or by providing a class to instantiate reflectively.

Nested property paths, including indexed properties in maps and collections, can be referenced by the org.springframework.batch.item.file.transform.FieldSet names. They will be converted to nested bean properties inside the prototype. The org.springframework.batch.item.file.transform.FieldSet 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.

To customize the way that org.springframework.batch.item.file.transform.FieldSet values are converted to the desired type for injecting into the prototype there are several choices. You can inject java.beans.PropertyEditor instances directly through the customEditors property, or you can override the createBinder(java.lang.Object) and initBinder(org.springframework.validation.DataBinder) methods, or you can provide a custom org.springframework.batch.item.file.transform.FieldSet implementation.

Property name matching is "fuzzy" in the sense that it tolerates close matches, as long as the match is unique. For instance:
  • Quantity = quantity (field names can be capitalised)
  • ISIN = isin (acronyms can be lower case bean property names, as per Java Beans recommendations)
  • DuckPate = duckPate (capitalisation including camel casing)
  • ITEM_ID = itemId (capitalisation and replacing word boundary with underscore)
  • ORDER.CUSTOMER_ID = order.customerId (nested paths are recursively checked)
The algorithm used to match a property name is to start with an exact match and then search successively through more distant matches until precisely one match is found. If more than one match is found there will be an error.

Author(s):
Dave Syer
 
 public class BeanWrapperFieldSetMapper<T> extends DefaultPropertyEditorRegistrar implements FieldSetMapper<T>,
 
 	private String name;
 
 	private Class<? extends T> type;
 
 
 	private static Map<Class<?>, Map<StringString>> propertiesMatched = new HashMap<Class<?>, Map<StringString>>();
 
 	private static int distanceLimit = 5;
	private boolean strict = true;
	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.springframework.beans.factory.BeanFactoryAware#setBeanFactory(org
	 * .springframework.beans.factory.BeanFactory)
	 */
	public void setBeanFactory(BeanFactory beanFactory) {
		this. = beanFactory;
	}

The bean name (id) for an object that can be populated from the field set that will be passed into mapFieldSet(org.springframework.batch.item.file.transform.FieldSet). Typically a prototype scoped bean so that a new instance is returned for each field set mapped. Either this property or the type property must be specified, but not both.

Parameters:
name the name of a prototype bean in the enclosing BeanFactory
	public void setPrototypeBeanName(String name) {
		this. = name;
	}

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 mapFieldSet(org.springframework.batch.item.file.transform.FieldSet).
Either this property or the prototype bean name must be specified, but not both.

Parameters:
type the type to set
	public void setTargetType(Class<? extends T> type) {
		this. = type;
	}

Check that precisely one of type or prototype bean name is specified.

	public void afterPropertiesSet() throws Exception {
		Assert.state( != null ||  != null"Either name or type must be provided.");
		Assert.state( == null ||  == null"Both name and type cannot be specified together.");
	}

Map the org.springframework.batch.item.file.transform.FieldSet to an object retrieved from the enclosing Spring context, or to a new instance of the required type if no prototype is available.

	public T mapFieldSet(FieldSet fsthrows BindException {
copy = getBean();
		DataBinder binder = createBinder(copy);
		if (binder.getBindingResult().hasErrors()) {
			throw new BindException(binder.getBindingResult());
		}
		return copy;
	}

Create a binder for the target object. The binder will then be used to bind the properties form a field set into the target object. This implementation creates a new org.springframework.validation.DataBinder and calls out to initBinder(org.springframework.validation.DataBinder) and org.springframework.batch.support.DefaultPropertyEditorRegistrar.registerCustomEditors(org.springframework.beans.PropertyEditorRegistry).

Parameters:
target
Returns:
a org.springframework.validation.DataBinder that can be used to bind properties to the target.
	protected DataBinder createBinder(Object target) {
		DataBinder binder = new DataBinder(target);
		initBinder(binder);
		return binder;
	}

Initialize a new binder instance. This hook allows customization of binder settings such as the direct field access. Called by createBinder(java.lang.Object).

Note that registration of custom property editors can be done in org.springframework.batch.support.DefaultPropertyEditorRegistrar.registerCustomEditors(org.springframework.beans.PropertyEditorRegistry).

Parameters:
binder new binder instance
See also:
createBinder(java.lang.Object)
	protected void initBinder(DataBinder binder) {
	}
	@SuppressWarnings("unchecked")
	private T getBean() {
		if ( != null) {
			return (T) .getBean();
		}
		try {
			return .newInstance();
		}
			ReflectionUtils.handleReflectionException(e);
		}
			ReflectionUtils.handleReflectionException(e);
		}
		// should not happen
		throw new IllegalStateException("Internal error: could not create bean instance for mapping.");
	}

Parameters:
bean
properties
Returns:
	@SuppressWarnings("unchecked")
	private Properties getBeanProperties(Object beanProperties properties) {
		Class<?> cls = bean.getClass();
		// Map from field names to property names
		Map<StringStringmatches = .get(cls);
		if (matches == null) {
			matches = new HashMap<StringString>();
			.put(clsmatches);
		}
		Set<Stringkeys = new HashSet(properties.keySet());
		for (String key : keys) {
			if (matches.containsKey(key)) {
				switchPropertyNames(propertieskeymatches.get(key));
				continue;
			}
			String name = findPropertyName(beankey);
			if (name != null) {
				matches.put(keyname);
				switchPropertyNames(propertieskeyname);
			}
		}
		return properties;
	}
	private String findPropertyName(Object beanString key) {
		if (bean == null) {
			return null;
		}
		Class<?> cls = bean.getClass();
		int index = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(key);
		String prefix;
		String suffix;
		// If the property name is nested recurse down through the properties
		// looking for a match.
		if (index > 0) {
			prefix = key.substring(0, index);
			suffix = key.substring(index + 1, key.length());
			String nestedName = findPropertyName(beanprefix);
			if (nestedName == null) {
				return null;
			}
			Object nestedValue = getPropertyValue(beannestedName);
			String nestedPropertyName = findPropertyName(nestedValuesuffix);
			return nestedPropertyName == null ? null : nestedName + "." + nestedPropertyName;
		}
		String name = null;
		int distance = 0;
		if (index > 0) {
			prefix = key.substring(0, index);
			suffix = key.substring(index);
		}
		else {
			prefix = key;
			suffix = "";
		}
		while (name == null && distance <= ) {
			String[] candidates = PropertyMatches.forProperty(prefixclsdistance).getPossibleMatches();
			// If we find precisely one match, then use that one...
			if (candidates.length == 1) {
				String candidate = candidates[0];
				if (candidate.equals(prefix)) { // if it's the same don't
					// replace it...
					name = key;
				}
				else {
					name = candidate + suffix;
				}
			}
			distance++;
		}
		return name;
	}
	private Object getPropertyValue(Object beanString nestedName) {
		BeanWrapperImpl wrapper = new BeanWrapperImpl(bean);
		Object nestedValue = wrapper.getPropertyValue(nestedName);
		if (nestedValue == null) {
			try {
				nestedValue = wrapper.getPropertyType(nestedName).newInstance();
				wrapper.setPropertyValue(nestedNamenestedValue);
			}
				ReflectionUtils.handleReflectionException(e);
			}
				ReflectionUtils.handleReflectionException(e);
			}
		}
		return nestedValue;
	}
	private void switchPropertyNames(Properties propertiesString oldNameString newName) {
		String value = properties.getProperty(oldName);
		properties.remove(oldName);
		properties.setProperty(newNamevalue);
	}

Public setter for the 'strict' property. If true, then mapFieldSet(org.springframework.batch.item.file.transform.FieldSet) will fail of the FieldSet contains fields that cannot be mapped to the bean.

Parameters:
strict
	public void setStrict(boolean strict) {
		this. = strict;
	}
New to GrepCode? Check out our FAQ X