Java tutorial
/* * Copyright 2001-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.commons.beanutils; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.StreamCorruptedException; import java.util.List; import java.util.Map; /** * <p>The metadata describing an individual property of a DynaBean.</p> * * <p>The meta contains an <em>optional</em> content type property ({@link #getContentType}) * for use by mapped and iterated properties. * A mapped or iterated property may choose to indicate the type it expects. * The DynaBean implementation may choose to enforce this type on its entries. * Alternatively, an implementatin may choose to ignore this property. * All keys for maps must be of type String so no meta data is needed for map keys.</p> * * @author Craig R. McClanahan * @version $Revision: 1.13 $ $Date: 2004/02/28 13:18:33 $ */ public class DynaProperty implements Serializable { // ----------------------------------------------------------- Constants /* * There are issues with serializing primitive class types on certain JVM versions * (including java 1.3). * This class uses a custom serialization implementation that writes an integer * for these primitive class. * This list of constants are the ones used in serialization. * If these values are changed, then older versions will no longer be read correctly */ private static final int BOOLEAN_TYPE = 1; private static final int BYTE_TYPE = 2; private static final int CHAR_TYPE = 3; private static final int DOUBLE_TYPE = 4; private static final int FLOAT_TYPE = 5; private static final int INT_TYPE = 6; private static final int LONG_TYPE = 7; private static final int SHORT_TYPE = 8; // ----------------------------------------------------------- Constructors /** * Construct a property that accepts any data type. * * @param name Name of the property being described */ public DynaProperty(String name) { this(name, Object.class); } /** * Construct a property of the specified data type. * * @param name Name of the property being described * @param type Java class representing the property data type */ public DynaProperty(String name, Class type) { super(); this.name = name; this.type = type; } /** * Construct an indexed or mapped <code>DynaProperty</code> that supports (pseudo)-introspection * of the content type. * * @param name Name of the property being described * @param type Java class representing the property data type * @param contentType Class that all indexed or mapped elements are instances of */ public DynaProperty(String name, Class type, Class contentType) { super(); this.name = name; this.type = type; this.contentType = contentType; } // ------------------------------------------------------------- Properties /** Property name */ protected String name = null; /** * Get the name of this property. */ public String getName() { return (this.name); } /** Property type */ protected transient Class type = null; /** * <p>Gets the Java class representing the data type of the underlying property * values.</p> * * <p>There are issues with serializing primitive class types on certain JVM versions * (including java 1.3). * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> * * <p><strong>Please leave this field as <code>transient</code></strong></p> */ public Class getType() { return (this.type); } /** The <em>(optional)</em> type of content elements for indexed <code>DynaProperty</code> */ protected transient Class contentType; /** * Gets the <em>(optional)</em> type of the indexed content for <code>DynaProperty</code>'s * that support this feature. * * <p>There are issues with serializing primitive class types on certain JVM versions * (including java 1.3). * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> * * @return the Class for the content type if this is an indexed <code>DynaProperty</code> * and this feature is supported. Otherwise null. */ public Class getContentType() { return contentType; } // --------------------------------------------------------- Public Methods /** * Does this property represent an indexed value (ie an array or List)? */ public boolean isIndexed() { if (type == null) { return (false); } else if (type.isArray()) { return (true); } else if (List.class.isAssignableFrom(type)) { return (true); } else { return (false); } } /** * Does this property represent a mapped value (ie a Map)? */ public boolean isMapped() { if (type == null) { return (false); } else { return (Map.class.isAssignableFrom(type)); } } /** * Return a String representation of this Object. */ public String toString() { StringBuffer sb = new StringBuffer("DynaProperty[name="); sb.append(this.name); sb.append(",type="); sb.append(this.type); if (isMapped() || isIndexed()) { sb.append(" <").append(this.contentType).append(">"); } sb.append("]"); return (sb.toString()); } // --------------------------------------------------------- Serialization helper methods /** * Writes this object safely. * There are issues with serializing primitive class types on certain JVM versions * (including java 1.3). * This method provides a workaround. */ private void writeObject(ObjectOutputStream out) throws IOException { writeAnyClass(this.type, out); if (isMapped() || isIndexed()) { writeAnyClass(this.contentType, out); } // write out other values out.defaultWriteObject(); } /** * Write a class using safe encoding to workaround java 1.3 serialization bug. */ private void writeAnyClass(Class clazz, ObjectOutputStream out) throws IOException { // safely write out any class int primitiveType = 0; if (Boolean.TYPE.equals(clazz)) { primitiveType = BOOLEAN_TYPE; } else if (Byte.TYPE.equals(clazz)) { primitiveType = BYTE_TYPE; } else if (Character.TYPE.equals(clazz)) { primitiveType = CHAR_TYPE; } else if (Double.TYPE.equals(clazz)) { primitiveType = DOUBLE_TYPE; } else if (Float.TYPE.equals(clazz)) { primitiveType = FLOAT_TYPE; } else if (Integer.TYPE.equals(clazz)) { primitiveType = INT_TYPE; } else if (Long.TYPE.equals(clazz)) { primitiveType = LONG_TYPE; } else if (Short.TYPE.equals(clazz)) { primitiveType = SHORT_TYPE; } if (primitiveType == 0) { // then it's not a primitive type out.writeBoolean(false); out.writeObject(clazz); } else { // we'll write out a constant instead out.writeBoolean(true); out.writeInt(primitiveType); } } /** * Reads field values for this object safely. * There are issues with serializing primitive class types on certain JVM versions * (including java 1.3). * This method provides a workaround. * * @throws StreamCorruptedException when the stream data values are outside expected range */ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { this.type = readAnyClass(in); if (isMapped() || isIndexed()) { this.contentType = readAnyClass(in); } // read other values in.defaultReadObject(); } /** * Reads a class using safe encoding to workaround java 1.3 serialization bug. */ private Class readAnyClass(ObjectInputStream in) throws IOException, ClassNotFoundException { // read back type class safely if (in.readBoolean()) { // it's a type constant switch (in.readInt()) { case BOOLEAN_TYPE: return Boolean.TYPE; case BYTE_TYPE: return Byte.TYPE; case CHAR_TYPE: return Character.TYPE; case DOUBLE_TYPE: return Double.TYPE; case FLOAT_TYPE: return Float.TYPE; case INT_TYPE: return Integer.TYPE; case LONG_TYPE: return Long.TYPE; case SHORT_TYPE: return Short.TYPE; default: // something's gone wrong throw new StreamCorruptedException( "Invalid primitive type. " + "Check version of beanutils used to serialize is compatible."); } } else { // it's another class return ((Class) in.readObject()); } } }