001: /*
002: * $Header: /export/home/cvsroot/MyPersonalizerRepository/MyPersonalizer/Subsystems/Kernel/Sources/es/udc/mypersonalizer/kernel/model/properties/SimpleProperty.java,v 1.1.1.1 2004/03/25 12:08:37 fbellas Exp $
003: * $Revision: 1.1.1.1 $
004: * $Date: 2004/03/25 12:08:37 $
005: *
006: * =============================================================================
007: *
008: * Copyright (c) 2003, The MyPersonalizer Development Group
009: * (http://www.tic.udc.es/~fbellas/mypersonalizer/index.html) at
010: * University Of A Coruna
011: * All rights reserved.
012: *
013: * Redistribution and use in source and binary forms, with or without
014: * modification, are permitted provided that the following conditions are met:
015: *
016: * - Redistributions of source code must retain the above copyright notice,
017: * this list of conditions and the following disclaimer.
018: *
019: * - Redistributions in binary form must reproduce the above copyright notice,
020: * this list of conditions and the following disclaimer in the documentation
021: * and/or other materials provided with the distribution.
022: *
023: * - Neither the name of the University Of A Coruna nor the names of its
024: * contributors may be used to endorse or promote products derived from
025: * this software without specific prior written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
028: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
029: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
030: * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
031: * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
032: * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
033: * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
034: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
035: * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
036: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
037: * POSSIBILITY OF SUCH DAMAGE.
038: *
039: */
040:
041: package es.udc.mypersonalizer.kernel.model.properties;
042:
043: import java.beans.PropertyEditorManager;
044: import java.beans.PropertyEditor;
045: import java.util.Collection;
046: import java.util.ArrayList;
047: import java.io.IOException;
048: import java.io.ObjectInputStream;
049:
050: /**
051: * A standard leaf implementation of the <code>Property</code> interface.
052: * This class stores internally the values of a property as an
053: * <code>Object[]</code>, and makes use of JavaBeans property editors to do
054: * the conversion from string values to object values and viceversa. So,
055: * if a simple property represents an array of objects of class "C",
056: * a JavaBeans property editor (see {@link PropertyEditor}) for
057: * such a class must be registered in {@link PropertyEditorManager}.
058: * <p>
059: * To make life easier, when the Java type of the object values is a
060: * wrapper version of a primitive type (example: <code>Integer</code>,
061: * <code>Float</code>, and so on), the implementation will look for a
062: * JavaBean Property editor corresponding to the primitive type (example:
063: * <code>int</code>, <code>float</code>, and so on) instead of the wrapper
064: * type. So, for example, if the type of the object values is
065: * <code>java.lang.Integer</code>, the implementation will try to find a
066: * a propety editor for "int", and not for "java.lang.Integer". This is done
067: * so because JSDK provides default JavaBeans property editors for Java
068: * primitive types, but <b>not</b> for their wrapper counterparts. Note also,
069: * that JSDK also provides property editors for some other very used classes
070: * (example: <code>java.lang.String</code>), as documented in
071: * <code>java.beans.PropertyEditorManager</code>. Summing up, in most cases,
072: * the programmer will not have to provide any JavaBeans property editor.
073: * <p>
074: * <b>NOTE:</b> the objects passed to this class in the <code>Object[]</code>
075: * must provide a suitable implementation of <code>hashCode()</code> and
076: * <code>equals(Object)</code>. This is mandatory in order to handle property
077: * comparisons properly. Two simple properties are equal if their simple names
078: * and their <code>Object[]</code> are equal.
079: *
080: * @author Fernando Bellas
081: * @see java.beans.PropertyEditor
082: * @see java.beans.PropertyEditorManager
083: * @since 1.0
084: */
085: public class SimpleProperty extends AbstractProperty implements
086: Property {
087:
088: /**
089: * For interoperability with possible future changes.
090: *
091: */
092: private static final long serialVersionUID = 8894113448792337116L;
093:
094: /**
095: * An array of <code>Object</code> representing the values of this
096: * property.
097: *
098: * @serial
099: */
100: private Object[] values;
101:
102: /**
103: * The JavaBeans property editor for the objects in <code>values</code>.
104: */
105: private transient PropertyEditor propertyEditor;
106:
107: /**
108: * The type of the objects in <code>values</code>.
109: */
110: private transient Class valueClass;
111:
112: /**
113: * Creates an instance of <code>SimpleProperty</code> from its name
114: * and its initial values. It is possible to pass an array of 0 elements
115: * for the initial values (for example: <code>new Integer[0]</code>).
116: *
117: * @param simpleName the simple name
118: * @param values the object values
119: * @throws PropertyEditorNotFoundException if there is no JavaBeans
120: * property editor for the type of the values registered in
121: * <code>java.beans.PropertyEditorManager</code>
122: */
123: public SimpleProperty(String simpleName, Object[] values)
124: throws PropertyEditorNotFoundException {
125:
126: super (simpleName);
127:
128: this .values = values;
129: valueClass = values.getClass().getComponentType();
130: propertyEditor = findPropertyEditor(valueClass);
131:
132: }
133:
134: /**
135: * Creates an instance of <code>SimpleProperty</code> from its name,
136: * its initial values as strings and the type of the values. It is
137: * possible to pass an array of 0 elements for the initial values
138: * (<code>new String[0]</code> is legal). The type of the values can not
139: * be a primitive type (<code>int</code>, <code>float</code>, and son on).
140: *
141: * @param simpleName the simple name
142: * @param valuesAsString the values of the property as an array of
143: * <code>String</code>
144: * @param valueClass the type of the objects in
145: * <code>valuesAsString</code>
146: * @throws PropertyEditorNotFoundException if there is no JavaBeans
147: * property editor for the type of the values registered in
148: * <code>java.beans.PropertyEditorManager</code>
149: * @throws IllegalStringValuesException if one or more strings in
150: * <code>valuesAsString</code> are illegal
151: * @throws IllegalArgumentException if <code>valueClass</code> is
152: * a primitive type (<code>int</code>, <code>float</code>, and
153: * so on)
154: */
155: public SimpleProperty(String simpleName, String[] valuesAsString,
156: Class valueClass) throws PropertyEditorNotFoundException,
157: IllegalStringValuesException {
158:
159: super (simpleName);
160:
161: if (valueClass.isPrimitive()) {
162: throw new IllegalArgumentException(
163: "Trying to use a primitive "
164: + "type; use a wrapper version instead");
165: }
166:
167: this .valueClass = valueClass;
168: propertyEditor = findPropertyEditor(valueClass);
169: setValuesAsString(valuesAsString);
170:
171: }
172:
173: /**
174: * Finds a JavaBeans property editor for a given type.
175: *
176: * @param theClass the type a property editor is requested for
177: * @return the property editor for the specified type
178: * @throws PropertyEditorNotFoundException if there is no JavaBeans
179: * property editor for such a type registered in
180: * <code>java.beans.PropertyManager</code>
181: */
182: private PropertyEditor findPropertyEditor(Class theClass)
183: throws PropertyEditorNotFoundException {
184:
185: Class theClassToUse;
186: PropertyEditor thePropertyEditor;
187:
188: /*
189: * Determine the class, "theClassToUse", to use with
190: * "PropertyEditorManager", as specified in the above general class
191: * comments. That is, if "theClass" corresponds to a wrapper class for
192: * a primitive type, then use the latter instead. Otherwise, use the
193: * class passed as parameter ("theClass").
194: */
195: if (theClass.equals(Boolean.class)) {
196: theClassToUse = boolean.class;
197: } else if (theClass.equals(Character.class)) {
198: theClassToUse = char.class;
199: } else if (theClass.equals(Byte.class)) {
200: theClassToUse = byte.class;
201: } else if (theClass.equals(Short.class)) {
202: theClassToUse = short.class;
203: } else if (theClass.equals(Integer.class)) {
204: theClassToUse = int.class;
205: } else if (theClass.equals(Long.class)) {
206: theClassToUse = long.class;
207: } else if (theClass.equals(Float.class)) {
208: theClassToUse = float.class;
209: } else if (theClass.equals(Double.class)) {
210: theClassToUse = double.class;
211: } else {
212: theClassToUse = theClass;
213: }
214:
215: /* Find the JavaBeans property editor. */
216: thePropertyEditor = PropertyEditorManager
217: .findEditor(theClassToUse);
218: if (thePropertyEditor == null) {
219: throw new PropertyEditorNotFoundException(theClassToUse
220: .getName());
221: }
222: return thePropertyEditor;
223: }
224:
225: /**
226: * Restores object state by first using the default serialization, and
227: * then restoring transient attributes.
228: *
229: * @param in the input stream where the serialized state is read from
230: * @throws java.io.IOException if there is a problem with the input stream
231: * when deserializing
232: * @throws java.lang.ClassNotFoundException if a class can not be found
233: * when deserializing
234: * @throws PropertyEditorNotFoundException if there is no JavaBeans
235: * property editor for the type of object values registered in
236: * <code>java.beans.PropertyManager</code>
237: */
238: private void readObject(ObjectInputStream in) throws IOException,
239: ClassNotFoundException, PropertyEditorNotFoundException {
240:
241: in.defaultReadObject();
242: valueClass = values.getClass().getComponentType();
243: propertyEditor = findPropertyEditor(valueClass);
244:
245: }
246:
247: /**
248: * Supported.
249: */
250: public String[] getValuesAsString() {
251: String[] valuesAsString = new String[values.length];
252:
253: /* Convert values from Object to String. */
254: for (int i = 0; i < values.length; i++) {
255: propertyEditor.setValue(values[i]);
256: valuesAsString[i] = propertyEditor.getAsText();
257: }
258:
259: return valuesAsString;
260: }
261:
262: /**
263: * Supported.
264: */
265: public void setValuesAsString(String[] valuesAsString)
266: throws IllegalStringValuesException {
267:
268: Collection illegalStringValues = new ArrayList();
269: int newSize;
270: Object[] newValues;
271:
272: newSize = valuesAsString.length;
273: newValues = (Object[]) java.lang.reflect.Array.newInstance(
274: valueClass, newSize);
275:
276: /* Try to convert the values from String to Object. */
277: for (int i = 0; i < valuesAsString.length; i++) {
278: try {
279: propertyEditor.setAsText(valuesAsString[i]);
280: newValues[i] = propertyEditor.getValue();
281: } catch (IllegalArgumentException e) {
282: illegalStringValues.add(valuesAsString[i]);
283: }
284: }
285:
286: /*
287: * Check if there were illegal values. If so, an exception is thrown.
288: * Otherwise, the values of this object are updated.
289: */
290: if (illegalStringValues.size() != 0) {
291: throw new IllegalStringValuesException(
292: (String[]) illegalStringValues
293: .toArray(new String[0]));
294: }
295: values = newValues;
296: }
297:
298: /**
299: * Supported. The implementation returns the values of the property as an
300: * array of <code>Object</code>, with the appropriate run-time type. So,
301: * the following line is legal:
302: * <p>
303: * <code>
304: * Integer[] values = (Integer[]) property.getValuesAsObject();
305: * </code>
306: * <p>
307: * where <code>property</code> encapsulates an array of
308: * <code>Integer</code> as its values.
309: */
310: public Object[] getValuesAsObject() {
311: return values;
312: }
313:
314: /**
315: * Supported. The run-time type of the values should be the same as that
316: * of the values currently stored (otherwise, it throws
317: * <code>java.lang.IllegalArgumentException</code>). It is possible to
318: * pass an array of 0 elements
319: * (for example: <code>setValuesAsObject(new Integer[0])</code>).
320: */
321: public void setValuesAsObject(Object[] values) {
322:
323: if (!values.getClass().getComponentType().equals(valueClass)) {
324: throw new IllegalArgumentException(
325: "Trying to change type of " + "values");
326: }
327:
328: this .values = values;
329: }
330:
331: /**
332: * Not supported.
333: */
334: public Property findProperty(String relativeName) {
335: throw new UnsupportedOperationException(
336: "Operation not supported");
337: }
338:
339: /**
340: * Returns the hash code of an instance of this class. The resulting
341: * hash code is the sum of property values hash codes.
342: *
343: * @return the hash code
344: */
345: public int hashCode() {
346: int hashCode = 0;
347: for (int i = 0; i < values.length; i++) {
348: hashCode += values[i].hashCode();
349: }
350: return hashCode;
351: }
352:
353: /**
354: * Returns if an instance of this class is equals to the one passed as a
355: * parameter. This method returns true if the property values for both
356: * properties are equal. The order of the property values must also be
357: * the same. Returns false otherwise.
358: *
359: * @param object the instance to be compared to.
360: * @return true if this instance and the one passed as a parameter are
361: * equal, false otherwise.
362: */
363: public boolean equals(Object object) {
364:
365: if (object == null) {
366: return false;
367: }
368:
369: SimpleProperty simpleProperty = null;
370: try {
371: simpleProperty = (SimpleProperty) object;
372:
373: } catch (ClassCastException e) {
374: return false;
375: }
376:
377: if (!getSimpleName().equals(simpleProperty.getSimpleName())) {
378: return false;
379: }
380:
381: /*
382: * Two simple properties are equal if the above conditions are
383: * satisfied and both the values and the order of the values match.
384: */
385: Object[] valuesToCompare = simpleProperty.getValuesAsObject();
386:
387: if (values.length != valuesToCompare.length) {
388: return false;
389: }
390:
391: boolean equals = true;
392: for (int i = 0; i < values.length && equals; i++) {
393: if (!values[i].equals(valuesToCompare[i])) {
394: equals = false;
395: }
396: }
397:
398: return equals;
399: }
400: }
|