KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > springframework > beans > propertyeditors > CustomMapEditor


1 /*
2  * Copyright 2002-2006 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16
17 package org.springframework.beans.propertyeditors;
18
19 import java.beans.PropertyEditorSupport JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.Map JavaDoc;
22 import java.util.SortedMap JavaDoc;
23 import java.util.TreeMap JavaDoc;
24
25 import org.springframework.core.CollectionFactory;
26
27 /**
28  * Property editor for Maps, converting any source Map
29  * to a given target Map type.
30  *
31  * @author Juergen Hoeller
32  * @since 2.0.1
33  * @see java.util.Map
34  * @see java.util.SortedMap
35  */

36 public class CustomMapEditor extends PropertyEditorSupport JavaDoc {
37
38     private final Class JavaDoc mapType;
39
40     private final boolean nullAsEmptyMap;
41
42
43     /**
44      * Create a new CustomMapEditor for the given target type,
45      * keeping an incoming <code>null</code> as-is.
46      * @param mapType the target type, which needs to be a
47      * sub-interface of Map or a concrete Map class
48      * @see java.util.Map
49      * @see java.util.HashMap
50      * @see java.util.TreeMap
51      * @see org.springframework.core.CollectionFactory#createLinkedMapIfPossible
52      */

53     public CustomMapEditor(Class JavaDoc mapType) {
54         this(mapType, false);
55     }
56
57     /**
58      * Create a new CustomMapEditor for the given target type.
59      * <p>If the incoming value is of the given type, it will be used as-is.
60      * If it is a different Map type or an array, it will be converted
61      * to a default implementation of the given Map type.
62      * If the value is anything else, a target Map with that single
63      * value will be created.
64      * <p>The default Map implementations are: ArrayList for List,
65      * TreeSet for SortedSet, and LinkedHashSet or HashSet for Set.
66      * @param mapType the target type, which needs to be a
67      * sub-interface of Map or a concrete Map class
68      * @param nullAsEmptyMap ap whether to convert an incoming <code>null</code>
69      * value to an empty Map (of the appropriate type)
70      * @see java.util.Map
71      * @see java.util.HashMap
72      * @see java.util.TreeMap
73      * @see org.springframework.core.CollectionFactory#createLinkedMapIfPossible
74      */

75     public CustomMapEditor(Class JavaDoc mapType, boolean nullAsEmptyMap) {
76         if (mapType == null) {
77             throw new IllegalArgumentException JavaDoc("Map type is required");
78         }
79         if (!Map JavaDoc.class.isAssignableFrom(mapType)) {
80             throw new IllegalArgumentException JavaDoc(
81                     "Map type [" + mapType.getName() + "] does not implement [java.util.Map]");
82         }
83         this.mapType = mapType;
84         this.nullAsEmptyMap = nullAsEmptyMap;
85     }
86
87
88     /**
89      * Convert the given text value to a Map with a single element.
90      */

91     public void setAsText(String JavaDoc text) throws IllegalArgumentException JavaDoc {
92         setValue(text);
93     }
94
95     /**
96      * Convert the given value to a Map of the target type.
97      */

98     public void setValue(Object JavaDoc value) {
99         if (value == null && this.nullAsEmptyMap) {
100             super.setValue(createMap(this.mapType, 0));
101         }
102         else if (value == null || (this.mapType.isInstance(value) && !alwaysCreateNewMap())) {
103             // Use the source value as-is, as it matches the target type.
104
super.setValue(value);
105         }
106         else if (value instanceof Map JavaDoc) {
107             // Convert Map elements.
108
Map JavaDoc source = (Map JavaDoc) value;
109             Map JavaDoc target = createMap(this.mapType, source.size());
110             for (Iterator JavaDoc it = source.entrySet().iterator(); it.hasNext();) {
111                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
112                 target.put(convertKey(entry.getKey()), convertValue(entry.getValue()));
113             }
114             super.setValue(target);
115         }
116         else {
117             throw new IllegalArgumentException JavaDoc("Value cannot be converted to Map: " + value);
118         }
119     }
120
121     /**
122      * Create a Map of the given type, with the given
123      * initial capacity (if supported by the Map type).
124      * @param mapType a sub-interface of Map
125      * @param initialCapacity the initial capacity
126      * @return the new Map instance
127      */

128     protected Map JavaDoc createMap(Class JavaDoc mapType, int initialCapacity) {
129         if (!mapType.isInterface()) {
130             try {
131                 return (Map JavaDoc) mapType.newInstance();
132             }
133             catch (Exception JavaDoc ex) {
134                 throw new IllegalArgumentException JavaDoc(
135                         "Could not instantiate map class [" + mapType.getName() + "]: " + ex.getMessage());
136             }
137         }
138         else if (SortedMap JavaDoc.class.equals(mapType)) {
139             return new TreeMap JavaDoc();
140         }
141         else {
142             return CollectionFactory.createLinkedMapIfPossible(initialCapacity);
143         }
144     }
145
146     /**
147      * Return whether to always create a new Map,
148      * even if the type of the passed-in Map already matches.
149      * <p>Default is "false"; can be overridden to enforce creation of a
150      * new Map, for example to convert elements in any case.
151      * @see #convertKey
152      * @see #convertValue
153      */

154     protected boolean alwaysCreateNewMap() {
155         return false;
156     }
157
158     /**
159      * Hook to convert each encountered Map key.
160      * The default implementation simply returns the passed-in key as-is.
161      * <p>Can be overridden to perform conversion of certain keys,
162      * for example from String to Integer.
163      * <p>Only called if actually creating a new Map!
164      * This is by default not the case if the type of the passed-in Map
165      * already matches. Override {@link #alwaysCreateNewMap()} to
166      * enforce creating a new Map in every case.
167      * @param key the source key
168      * @return the key to be used in the target Map
169      * @see #alwaysCreateNewMap
170      */

171     protected Object JavaDoc convertKey(Object JavaDoc key) {
172         return key;
173     }
174
175     /**
176      * Hook to convert each encountered Map value.
177      * The default implementation simply returns the passed-in value as-is.
178      * <p>Can be overridden to perform conversion of certain values,
179      * for example from String to Integer.
180      * <p>Only called if actually creating a new Map!
181      * This is by default not the case if the type of the passed-in Map
182      * already matches. Override {@link #alwaysCreateNewMap()} to
183      * enforce creating a new Map in every case.
184      * @param value the source value
185      * @return the value to be used in the target Map
186      * @see #alwaysCreateNewMap
187      */

188     protected Object JavaDoc convertValue(Object JavaDoc value) {
189         return value;
190     }
191
192
193     /**
194      * This implementation returns <code>null</code> to indicate that
195      * there is no appropriate text representation.
196      */

197     public String JavaDoc getAsText() {
198         return null;
199     }
200
201 }
202
Popular Tags