1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.morph.transform.converters;
17
18 import java.util.Collection;
19 import java.util.Iterator;
20 import java.util.Locale;
21 import java.util.Map;
22 import java.util.Set;
23
24 import net.sf.composite.util.ObjectUtils;
25 import net.sf.morph.transform.DecoratedConverter;
26 import net.sf.morph.transform.TransformationException;
27 import net.sf.morph.transform.transformers.BaseTransformer;
28 import net.sf.morph.util.BidirectionalMap;
29 import net.sf.morph.util.ClassUtils;
30 import net.sf.morph.util.ContainerUtils;
31
32 /**
33 * A transformer which transforms defined arbitrary objects to other defined
34 * arbitrary objects based on this class' <code>mapping</code> property. For
35 * example, an arbitrary mapping might specify that the Integer 1 be converted
36 * to the String "one". As many mappings as are needed may be specified. By
37 * default, mappings are assumed to be bidirectional. Continuing with the
38 * example above, this means that in addition to the Integer 1 being mapped
39 * transformed to the String "one", the String "one" will be transformed to the
40 * Integer 1.
41 *
42 * @author Matt Sgarlata
43 * @since Apr 11, 2005
44 */
45 public class ArbitraryObjectMappingConverter extends BaseTransformer implements DecoratedConverter {
46
47 private boolean bidirectional = true;
48 private Map mapping;
49
50 /**
51 * {@inheritDoc}
52 */
53 protected Object convertImpl(Class destinationClass, Object source,
54 Locale locale) throws Exception {
55 if (getMapping().containsKey(source)) {
56 return getMapping().get(source);
57 }
58 if (isBidirectional()) {
59 BidirectionalMap bidirectionalMap = (BidirectionalMap) mapping;
60 if (bidirectionalMap.getReverseMap().containsKey(bidirectionalMap)) {
61 return bidirectionalMap.getKey(source);
62 }
63 }
64 throw new TransformationException(
65 "No mapping was specified for source object "
66 + ObjectUtils.getObjectDescription(source));
67 }
68
69 /**
70 * Get the forward classes, plus backward classes if this converter is bidi.
71 * @param forwardMappingKeys
72 * @param backwardMappingKeys
73 * @return Class[]
74 * @throws Exception not likely
75 */
76 protected Class[] getClasses(Collection forwardMappingKeys, Collection backwardMappingKeys) throws Exception {
77
78
79
80 int maxNumClasses = forwardMappingKeys.size();
81 if (isBidirectional()) {
82 maxNumClasses += backwardMappingKeys.size();
83 }
84
85 Set classes = ContainerUtils.createOrderedSet();
86 addContainedClasses(classes, forwardMappingKeys);
87
88 if (isBidirectional()) {
89 addContainedClasses(classes, backwardMappingKeys);
90 }
91
92 return (Class[]) classes.toArray(new Class[classes.size()]);
93 }
94
95 /**
96 * Add the classes of the contents of <code>objects</code> to <code>classes</code>.
97 * @param classes
98 * @param objects
99 */
100 protected void addContainedClasses(Set classes, Collection objects) {
101 if (objects != null) {
102 for (Iterator i = objects.iterator(); i.hasNext(); ) {
103 classes.add(ClassUtils.getClass(i.next()));
104 }
105 }
106 }
107
108 /**
109 * {@inheritDoc}
110 */
111 protected Class[] getSourceClassesImpl() throws Exception {
112 if (ObjectUtils.isEmpty(mapping)) {
113 throw new IllegalStateException("The mapping property of this converter must be set");
114 }
115 return getClasses(mapping.keySet(), mapping.values());
116 }
117
118 /**
119 * {@inheritDoc}
120 */
121 protected Class[] getDestinationClassesImpl() throws Exception {
122 if (ObjectUtils.isEmpty(mapping)) {
123 throw new IllegalStateException("The mapping property of this converter must be set");
124 }
125 return getClasses(mapping.values(), mapping.keySet());
126 }
127
128 /**
129 * {@inheritDoc}
130 */
131 protected boolean isWrappingRuntimeExceptions() {
132 return true;
133 }
134
135 /**
136 * Learn whether this ArbitraryObjectMappingConverter is bidirectional.
137 * @return boolean
138 */
139 public boolean isBidirectional() {
140 return bidirectional;
141 }
142
143 /**
144 * Set whether this ArbitraryObjectMappingConverter is bidirectional.
145 * @param bidirectional
146 */
147 public void setBidirectional(boolean bidirectional) {
148 setInitialized(false);
149 this.bidirectional = bidirectional;
150 }
151
152 /**
153 * Get the object mapping.
154 * @return Map
155 */
156 public Map getMapping() {
157 return mapping;
158 }
159
160 /**
161 * Set the mapping.
162 *
163 * @param mapping the mapping
164 * @throws IllegalArgumentException
165 * if <code>bidirectional</code> is <code>true</code> and
166 * there is some value which is mapped to more than one key. In
167 * this case, it is impossible to construct a bidirectional map
168 * because there is no way to determine to which key the value
169 * is mapped.
170 */
171 public void setMapping(Map mapping) {
172 setInitialized(false);
173 if (isBidirectional() && !(mapping instanceof BidirectionalMap)) {
174 this.mapping = new BidirectionalMap(mapping);
175 }
176 else {
177 this.mapping = mapping;
178 }
179 }
180
181 /**
182 * @return Map
183 * @deprecated
184 */
185 public Map getVisitedSourceToDestinationMap() {
186 return getMapping();
187 }
188
189 /**
190 * @param visitedSourceToDestinationMap
191 * @deprecated
192 */
193 public void setVisitedSourceToDestinationMap(Map visitedSourceToDestinationMap) {
194 throw new UnsupportedOperationException();
195 }
196
197 }