1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.beanutils;
19
20
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.io.ObjectOutputStream;
24 import java.io.ObjectInputStream;
25 import java.io.StreamCorruptedException;
26 import java.util.List;
27 import java.util.Map;
28
29
30 /**
31 * <p>The metadata describing an individual property of a DynaBean.</p>
32 *
33 * <p>The meta contains an <em>optional</em> content type property ({@link #getContentType})
34 * for use by mapped and iterated properties.
35 * A mapped or iterated property may choose to indicate the type it expects.
36 * The DynaBean implementation may choose to enforce this type on its entries.
37 * Alternatively, an implementatin may choose to ignore this property.
38 * All keys for maps must be of type String so no meta data is needed for map keys.</p>
39 *
40 * @author Craig R. McClanahan
41 * @version $Revision: 1.13 $ $Date: 2004/02/28 13:18:33 $
42 */
43
44 public class DynaProperty implements Serializable {
45
46
47
48
49
50
51
52
53
54
55
56 private static final int BOOLEAN_TYPE = 1;
57 private static final int BYTE_TYPE = 2;
58 private static final int CHAR_TYPE = 3;
59 private static final int DOUBLE_TYPE = 4;
60 private static final int FLOAT_TYPE = 5;
61 private static final int INT_TYPE = 6;
62 private static final int LONG_TYPE = 7;
63 private static final int SHORT_TYPE = 8;
64
65
66
67
68
69 /**
70 * Construct a property that accepts any data type.
71 *
72 * @param name Name of the property being described
73 */
74 public DynaProperty(String name) {
75
76 this(name, Object.class);
77
78 }
79
80
81 /**
82 * Construct a property of the specified data type.
83 *
84 * @param name Name of the property being described
85 * @param type Java class representing the property data type
86 */
87 public DynaProperty(String name, Class type) {
88
89 super();
90 this.name = name;
91 this.type = type;
92
93 }
94
95 /**
96 * Construct an indexed or mapped <code>DynaProperty</code> that supports (pseudo)-introspection
97 * of the content type.
98 *
99 * @param name Name of the property being described
100 * @param type Java class representing the property data type
101 * @param contentType Class that all indexed or mapped elements are instances of
102 */
103 public DynaProperty(String name, Class type, Class contentType) {
104
105 super();
106 this.name = name;
107 this.type = type;
108 this.contentType = contentType;
109
110 }
111
112
113
114 /** Property name */
115 protected String name = null;
116 /**
117 * Get the name of this property.
118 */
119 public String getName() {
120 return (this.name);
121 }
122
123 /** Property type */
124 protected transient Class type = null;
125 /**
126 * <p>Gets the Java class representing the data type of the underlying property
127 * values.</p>
128 *
129 * <p>There are issues with serializing primitive class types on certain JVM versions
130 * (including java 1.3).
131 * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p>
132 *
133 * <p><strong>Please leave this field as <code>transient</code></strong></p>
134 */
135 public Class getType() {
136 return (this.type);
137 }
138
139
140 /** The <em>(optional)</em> type of content elements for indexed <code>DynaProperty</code> */
141 protected transient Class contentType;
142 /**
143 * Gets the <em>(optional)</em> type of the indexed content for <code>DynaProperty</code>'s
144 * that support this feature.
145 *
146 * <p>There are issues with serializing primitive class types on certain JVM versions
147 * (including java 1.3).
148 * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p>
149 *
150 * @return the Class for the content type if this is an indexed <code>DynaProperty</code>
151 * and this feature is supported. Otherwise null.
152 */
153 public Class getContentType() {
154 return contentType;
155 }
156
157
158
159
160 /**
161 * Does this property represent an indexed value (ie an array or List)?
162 */
163 public boolean isIndexed() {
164
165 if (type == null) {
166 return (false);
167 } else if (type.isArray()) {
168 return (true);
169 } else if (List.class.isAssignableFrom(type)) {
170 return (true);
171 } else {
172 return (false);
173 }
174
175 }
176
177
178 /**
179 * Does this property represent a mapped value (ie a Map)?
180 */
181 public boolean isMapped() {
182
183 if (type == null) {
184 return (false);
185 } else {
186 return (Map.class.isAssignableFrom(type));
187 }
188
189 }
190
191
192 /**
193 * Return a String representation of this Object.
194 */
195 public String toString() {
196
197 StringBuffer sb = new StringBuffer("DynaProperty[name=");
198 sb.append(this.name);
199 sb.append(",type=");
200 sb.append(this.type);
201 if (isMapped() || isIndexed()) {
202 sb.append(" <").append(this.contentType).append(">");
203 }
204 sb.append("]");
205 return (sb.toString());
206
207 }
208
209
210
211 /**
212 * Writes this object safely.
213 * There are issues with serializing primitive class types on certain JVM versions
214 * (including java 1.3).
215 * This method provides a workaround.
216 */
217 private void writeObject(ObjectOutputStream out) throws IOException {
218
219 writeAnyClass(this.type,out);
220
221 if (isMapped() || isIndexed()) {
222 writeAnyClass(this.contentType,out);
223 }
224
225
226 out.defaultWriteObject();
227 }
228
229 /**
230 * Write a class using safe encoding to workaround java 1.3 serialization bug.
231 */
232 private void writeAnyClass(Class clazz, ObjectOutputStream out) throws IOException {
233
234 int primitiveType = 0;
235 if (Boolean.TYPE.equals(clazz)) {
236 primitiveType = BOOLEAN_TYPE;
237 } else if (Byte.TYPE.equals(clazz)) {
238 primitiveType = BYTE_TYPE;
239 } else if (Character.TYPE.equals(clazz)) {
240 primitiveType = CHAR_TYPE;
241 } else if (Double.TYPE.equals(clazz)) {
242 primitiveType = DOUBLE_TYPE;
243 } else if (Float.TYPE.equals(clazz)) {
244 primitiveType = FLOAT_TYPE;
245 } else if (Integer.TYPE.equals(clazz)) {
246 primitiveType = INT_TYPE;
247 } else if (Long.TYPE.equals(clazz)) {
248 primitiveType = LONG_TYPE;
249 } else if (Short.TYPE.equals(clazz)) {
250 primitiveType = SHORT_TYPE;
251 }
252
253 if (primitiveType == 0) {
254
255 out.writeBoolean(false);
256 out.writeObject(clazz);
257 } else {
258
259 out.writeBoolean(true);
260 out.writeInt(primitiveType);
261 }
262 }
263
264 /**
265 * Reads field values for this object safely.
266 * There are issues with serializing primitive class types on certain JVM versions
267 * (including java 1.3).
268 * This method provides a workaround.
269 *
270 * @throws StreamCorruptedException when the stream data values are outside expected range
271 */
272 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
273
274 this.type = readAnyClass(in);
275
276 if (isMapped() || isIndexed()) {
277 this.contentType = readAnyClass(in);
278 }
279
280
281 in.defaultReadObject();
282 }
283
284
285 /**
286 * Reads a class using safe encoding to workaround java 1.3 serialization bug.
287 */
288 private Class readAnyClass(ObjectInputStream in) throws IOException, ClassNotFoundException {
289
290 if (in.readBoolean()) {
291
292 switch (in.readInt()) {
293
294 case BOOLEAN_TYPE: return Boolean.TYPE;
295 case BYTE_TYPE: return Byte.TYPE;
296 case CHAR_TYPE: return Character.TYPE;
297 case DOUBLE_TYPE: return Double.TYPE;
298 case FLOAT_TYPE: return Float.TYPE;
299 case INT_TYPE: return Integer.TYPE;
300 case LONG_TYPE: return Long.TYPE;
301 case SHORT_TYPE: return Short.TYPE;
302 default:
303
304 throw new StreamCorruptedException(
305 "Invalid primitive type. "
306 + "Check version of beanutils used to serialize is compatible.");
307
308 }
309
310 } else {
311
312 return ((Class) in.readObject());
313 }
314 }
315 }