001: /*
002: * Copyright 2004-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.springframework.binding.mapping;
017:
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020: import org.springframework.binding.convert.ConversionExecutor;
021: import org.springframework.binding.expression.Expression;
022: import org.springframework.binding.expression.SettableExpression;
023: import org.springframework.core.style.ToStringCreator;
024: import org.springframework.util.Assert;
025:
026: /**
027: * A single mapping definition, encapulating the information neccessary to map
028: * the result of evaluating an expression on a source object to a property on a
029: * target object, optionally applying a type conversion during the mapping
030: * process.
031: *
032: * @author Keith Donald
033: */
034: public class Mapping implements AttributeMapper {
035:
036: private static final Log logger = LogFactory.getLog(Mapping.class);
037:
038: /**
039: * The source expression to evaluate against a source object to map from.
040: */
041: private final Expression sourceExpression;
042:
043: /**
044: * The target expression to set on a target object to map to.
045: */
046: private final SettableExpression targetExpression;
047:
048: /**
049: * A type converter to apply during the mapping process.
050: */
051: private final ConversionExecutor typeConverter;
052:
053: /**
054: * Whether or not this is a required mapping; if true, the source expression
055: * must return a non-null value.
056: */
057: private boolean required;
058:
059: /**
060: * Creates a new mapping.
061: * @param sourceExpression the source expression
062: * @param targetExpression the target expression
063: * @param typeConverter a type converter
064: */
065: public Mapping(Expression sourceExpression,
066: SettableExpression targetExpression,
067: ConversionExecutor typeConverter) {
068: this (sourceExpression, targetExpression, typeConverter, false);
069: }
070:
071: /**
072: * Creates a new mapping.
073: * @param sourceExpression the source expression
074: * @param targetExpression the target expression
075: * @param typeConverter a type converter
076: * @param required whether or not this mapping is required
077: */
078: protected Mapping(Expression sourceExpression,
079: SettableExpression targetExpression,
080: ConversionExecutor typeConverter, boolean required) {
081: Assert.notNull(sourceExpression,
082: "The source expression is required");
083: Assert.notNull(targetExpression,
084: "The target expression is required");
085: this .sourceExpression = sourceExpression;
086: this .targetExpression = targetExpression;
087: this .typeConverter = typeConverter;
088: this .required = required;
089: }
090:
091: /**
092: * Map the <code>sourceAttribute</code> in to the
093: * <code>targetAttribute</code> target map, performing type conversion if
094: * necessary.
095: * @param source The source data structure
096: * @param target The target data structure
097: */
098: public void map(Object source, Object target, MappingContext context) {
099: // get source value
100: Object sourceValue = sourceExpression.evaluate(source, null);
101: if (sourceValue == null) {
102: if (required) {
103: throw new RequiredMappingException(
104: "This mapping is required; evaluation of expression '"
105: + sourceExpression
106: + "' against source of type ["
107: + source.getClass()
108: + "] must return a non-null value");
109: } else {
110: // source expression returned no value, simply abort mapping
111: return;
112: }
113: }
114: Object targetValue = sourceValue;
115: if (typeConverter != null) {
116: targetValue = typeConverter.execute(sourceValue);
117: }
118: // set target value
119: if (logger.isDebugEnabled()) {
120: logger.debug("Mapping '" + sourceExpression + "' value ["
121: + sourceValue + "] to target property '"
122: + targetExpression
123: + "'; setting property value to [" + targetValue
124: + "]");
125: }
126: targetExpression.evaluateToSet(target, targetValue, null);
127: }
128:
129: public boolean equals(Object o) {
130: if (!(o instanceof Mapping)) {
131: return false;
132: }
133: Mapping other = (Mapping) o;
134: return sourceExpression.equals(other.sourceExpression)
135: && targetExpression.equals(other.targetExpression);
136: }
137:
138: public int hashCode() {
139: return sourceExpression.hashCode()
140: + targetExpression.hashCode();
141: }
142:
143: public String toString() {
144: return new ToStringCreator(this ).append(
145: sourceExpression + " -> " + targetExpression)
146: .toString();
147: }
148: }
|