1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.transform.copiers;
17
18 import java.util.ArrayList;
19 import java.util.Arrays;
20 import java.util.HashSet;
21 import java.util.List;
22 import java.util.Locale;
23 import java.util.Set;
24
25 import net.sf.composite.util.ObjectUtils;
26 import net.sf.morph.reflect.ReflectionException;
27 import net.sf.morph.transform.TransformationException;
28 import net.sf.morph.util.ContainerUtils;
29 import net.sf.morph.util.StringUtils;
30
31 /**
32 * <p>Copies the properties specified by the <code>propertiesToCopy</code>
33 * property of this class from the source to the destination. If
34 * <code>propertiesToCopy</code> is not specified, all of the source
35 * properties will be copied to the destination.
36 *
37 * <p>Copies properties that have the same name from the source to the destination.
38 * By default, if a property found on the source is missing on the destination,
39 * an exception will <em>not</em> be thrown and copying will continue. If you
40 * want to ensure all properties from the source are copied to the destination,
41 * set the <em>errorOnMissingProperty</em> property of this class to
42 * <code>true</code>.
43 *
44 * @author Matt Sgarlata
45 * @author Alexander Volanis
46 * @since Oct 31, 2004
47 */
48 public class PropertyNameMatchingCopier extends BasePropertyNameCopier {
49
50 private Set propertiesToCopy = ContainerUtils.createOrderedSet();
51 private Set propertiesToIgnore = ContainerUtils.createOrderedSet();
52
53 /**
54 * Create a new PropertyNameMatchingCopier.
55 */
56 public PropertyNameMatchingCopier() {
57 super();
58 setErrorOnMissingProperty(false);
59 }
60
61 /**
62 * Create a new PropertyNameMatchingCopier.
63 * @param errorOnMissingProperty
64 */
65 public PropertyNameMatchingCopier(boolean errorOnMissingProperty) {
66 super(errorOnMissingProperty);
67 }
68
69 /**
70 * {@inheritDoc}
71 */
72 public void copyImpl(Object destination, Object source, Locale locale,
73 Integer preferredTransformationType) throws Exception {
74 String[] properties = evaluateIncludedProperties(source);
75 if (log.isInfoEnabled()) {
76 if (ObjectUtils.isEmpty(properties)) {
77 getLog().info("No properties available for copying");
78 }
79 else {
80 getLog()
81 .info("Copying properties " + StringUtils.englishJoin(properties));
82 }
83 }
84
85 List unreadableProperties = null;
86 List unwriteableProperties = null;
87 if (isErrorOnMissingProperty() || getLog().isTraceEnabled()) {
88 unreadableProperties = new ArrayList();
89 unwriteableProperties = new ArrayList();
90 }
91 for (int i = 0; i < properties.length; i++) {
92 String property = properties[i];
93 boolean sourceReadable = getBeanReflector().isReadable(source, property);
94 boolean destinationWriteable = getBeanReflector().isWriteable(destination,
95 property);
96
97 if (sourceReadable && destinationWriteable) {
98 copyProperty(property, source, property, destination, locale,
99 preferredTransformationType);
100 }
101 else {
102
103
104 if (isErrorOnMissingProperty() || getLog().isTraceEnabled()) {
105 if (!sourceReadable) {
106 unreadableProperties.add(property);
107 }
108 if (!destinationWriteable) {
109 unwriteableProperties.add(property);
110 }
111 }
112 }
113 }
114
115 if (isErrorOnMissingProperty() || getLog().isTraceEnabled()) {
116 int skippedPropertiesSize = unreadableProperties.size()
117 + unwriteableProperties.size();
118 List skippedProperties = new ArrayList(skippedPropertiesSize);
119 skippedProperties.addAll(unreadableProperties);
120 skippedProperties.addAll(unwriteableProperties);
121
122 String message = "The following properties were not copied "
123 + "because they were not readable on the source object, not "
124 + "writeable on the destination object or both: "
125 + StringUtils.englishJoin(skippedProperties)
126 + ". The properties that were not readable are: "
127 + StringUtils.englishJoin(unreadableProperties)
128 + ". The properties that were not writeable are: "
129 + StringUtils.englishJoin(unwriteableProperties);
130 if (isErrorOnMissingProperty()) {
131 throw new TransformationException(message);
132 }
133
134
135 if (!skippedProperties.isEmpty()) {
136 getLog().trace(message);
137 }
138 }
139 }
140
141 /**
142 * Get the properties to copy.
143 * @return String[]
144 */
145 public synchronized String[] getPropertiesToCopy() {
146 return (String[]) propertiesToCopy.toArray(new String[propertiesToCopy.size()]);
147 }
148
149 /**
150 * Set the properties to copy.
151 * @param propertiesToCopy String[]
152 */
153 public synchronized void setPropertiesToCopy(String[] propertiesToCopy) {
154 this.propertiesToCopy.clear();
155 this.propertiesToCopy.addAll(Arrays.asList(propertiesToCopy));
156 }
157
158 /**
159 * Add a property to copy.
160 * @param propertyName
161 */
162 public synchronized void addPropertyToCopy(String propertyName) {
163 propertiesToCopy.add(propertyName);
164 }
165
166 /**
167 * Get the properties to ignore.
168 * @return String[]
169 */
170 public synchronized String[] getPropertiesToIgnore() {
171 return (String[]) propertiesToIgnore
172 .toArray(new String[propertiesToIgnore.size()]);
173 }
174
175 /**
176 * Set the properties to ignore.
177 * @param propertiesToIgnore String[]
178 */
179 public synchronized void setPropertiesToIgnore(String[] propertiesToIgnore) {
180 this.propertiesToIgnore.clear();
181 this.propertiesToIgnore.addAll(Arrays.asList(propertiesToIgnore));
182 }
183
184 /**
185 * Add a property to ignore.
186 * @param propertyName
187 */
188 public synchronized void addPropertyToIgnore(String propertyName) {
189 propertiesToIgnore.add(propertyName);
190 }
191
192 /**
193 * {@inheritDoc}
194 */
195 protected boolean isImpreciseTransformationImpl(Class destinationClass,
196 Class sourceClass) {
197
198 if (!isErrorOnMissingProperty() && ObjectUtils.isEmpty(propertiesToCopy)
199 && ObjectUtils.isEmpty(propertiesToIgnore)) {
200 Object sourceBean;
201 Object destinationBean;
202 try {
203 sourceBean = getInstantiatingReflector().newInstance(sourceClass, null);
204 destinationBean = getInstantiatingReflector().newInstance(destinationClass, null);
205 } catch (ReflectionException e) {
206 return true;
207 }
208 HashSet sourcePropertyNames = new HashSet(Arrays.asList(getBeanReflector().getPropertyNames(sourceBean)));
209 HashSet destinationPropertyNames = new HashSet(Arrays.asList(getBeanReflector().getPropertyNames(destinationBean)));
210 return !sourcePropertyNames.equals(destinationPropertyNames);
211 }
212 return super.isImpreciseTransformationImpl(destinationClass, sourceClass);
213 }
214
215 /**
216 * Get the included properties for the given object.
217 * @param source
218 * @return String[]
219 */
220 private String[] evaluateIncludedProperties(Object source) {
221 Set result = ContainerUtils.createOrderedSet();
222 result.addAll(propertiesToCopy);
223 result.retainAll(propertiesToIgnore);
224 if (!result.isEmpty()) {
225 throw new IllegalStateException("Overlapping included/ignored properties: "
226 + result);
227 }
228 if (ObjectUtils.isEmpty(propertiesToCopy)) {
229 result.addAll(Arrays.asList(getBeanReflector().getPropertyNames(source)));
230 result.removeAll(propertiesToIgnore);
231 }
232 else {
233 result = propertiesToCopy;
234 }
235 return (String[]) result.toArray(new String[result.size()]);
236 }
237 }