1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 package com.sun.star.wizards.ui.event;
24 
25 import java.lang.reflect.InvocationTargetException;
26 import java.lang.reflect.Method;
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.Iterator;
30 import com.sun.star.wizards.common.PropertyNames;
31 
32 /**
33  * @author rpiterman
34  * DataAware objects are used to live-synchronize UI and DataModel/DataObject.
35  * It is used as listener on UI events, to keep the DataObject up to date.
36  * This class, as a base abstract class, sets a frame of functionality,
37  * delegating the data Object get/set methods to a Value object,
38  * and leaving the UI get/set methods abstract.
39  * Note that event listenning is *not* a part of this model.
40  * the updateData() or updateUI() methods should be porogramatically called.
41  * in child classes, the updateData() will be binded to UI event calls.
42  * <br><br>
43  * This class holds references to a Data Object and a Value object.
44  * The Value object "knows" how to get and set a value from the
45  * Data Object.
46  */
47 public abstract class DataAware {
48 
49     /**
50      * this is the data object.
51      */
52     protected Object dataObject;
53     //protected Method setMethod;
54     //protected Method getMethod;
55     /**
56      * A Value Object knows how to get/set a value
57      * from/to the data object.
58      */
59     protected Value value;
60 
61     /**
62      * creates a DataAware object for the given data object and Value object.
63      * @param dataObject_
64      * @param value_
65      */
DataAware(Object dataObject_, Value value_)66     protected DataAware(Object dataObject_, Value value_) {
67         dataObject = dataObject_;
68         value = value_;
69         //getMethod = createGetMethod(dataPropName, dataObject);
70         //setMethod = createSetMethod(dataPropName, dataObject, getMethod.getReturnType());
71     }
72 
73     /**
74      * returns the data object.
75      * @return
76      */
getDataObject()77     public Object getDataObject() {
78         return dataObject;
79     }
80 
81     /**
82      * sets a new data object. Optionally
83      * update the UI.
84      * @param obj the new data object.
85      * @param updateUI if true updateUI() will be called.
86      */
setDataObject(Object obj, boolean updateUI)87     public void setDataObject(Object obj, boolean updateUI) {
88 
89         if (obj != null && !value.isAssignable(obj.getClass()))
90             throw new ClassCastException("can not cast new DataObject to original Class");
91 
92         dataObject = obj;
93 
94         if (updateUI)
95             updateUI();
96 
97     }
98 
99     /**
100      * Sets the given value to the data object.
101      * this method delegates the job to the
102      * Value object, but can be overwritten if
103      * another kind of Data is needed.
104      * @param newValue the new value to set to the DataObject.
105      */
setToData(Object newValue)106     protected void setToData(Object newValue) {
107         value.set(newValue,getDataObject());
108     }
109 
110     /**
111      * gets the current value from the data obejct.
112      * this method delegates the job to
113      * the value object.
114      * @return the current value of the data object.
115      */
getFromData()116     protected Object getFromData() {
117         return value.get(getDataObject());
118     }
119 
120     /**
121      * sets the given value to the UI control
122      * @param newValue the value to set to the ui control.
123      */
setToUI(Object newValue)124     protected abstract void setToUI(Object newValue);
125 
126     /**
127      * gets the current value from the UI control.
128      * @return the current value from the UI control.
129      */
getFromUI()130     protected abstract Object getFromUI();
131 
132     /**
133      * updates the UI control according to the
134      * current state of the data object.
135      */
updateUI()136     public void updateUI() {
137         Object data = getFromData();
138         Object ui = getFromUI();
139         if (!equals(data, ui))
140             try {
141                 setToUI(data);
142             } catch (Exception ex) {
143                 ex.printStackTrace();
144                 //TODO tell user...
145             }
146         enableControls(data);
147     }
148 
149     /**
150      * enables
151      * @param currentValue
152      */
enableControls(Object currentValue)153     protected void enableControls(Object currentValue) {
154     }
155 
156     /**
157      * updates the DataObject according to
158      * the current state of the UI control.
159      */
updateData()160     public void updateData() {
161         Object data = getFromData();
162         Object ui = getFromUI();
163         if (!equals(data, ui))
164             setToData(ui);
165         enableControls(ui);
166     }
167 
168     public interface Listener {
eventPerformed(Object event)169         public void eventPerformed(Object event);
170     }
171 
172     /**
173      * compares the two given objects.
174      * This method is null safe and returns true also if both are null...
175      * If both are arrays, treats them as array of short and compares them.
176      * @param a first object to compare
177      * @param b second object to compare.
178      * @return true if both are null or both are equal.
179      */
equals(Object a, Object b)180     protected boolean equals(Object a, Object b) {
181         if (a == null && b == null)
182             return true;
183         if (a == null || b == null)
184             return false;
185         if (a.getClass().isArray()) {
186             if (b.getClass().isArray())
187                 return Arrays.equals((short[]) a, (short[]) b);
188             else
189                 return false;
190         }
191         return a.equals(b);
192     }
193 
194     /**
195      * given a collection containing DataAware objects,
196      * calls updateUI() on each memebr of the collection.
197      * @param dataAwares a collection containing DataAware objects.
198      */
updateUI(Collection dataAwares)199     public static void updateUI(Collection dataAwares) {
200         for (Iterator i = dataAwares.iterator(); i.hasNext();)
201              ((DataAware) i.next()).updateUI();
202     }
203 
updateData(Collection dataAwares)204     public static void updateData(Collection dataAwares) {
205         for (Iterator i = dataAwares.iterator(); i.hasNext();)
206              ((DataAware) i.next()).updateData();
207     }
208 
209     /**
210      * /**
211      * Given a collection containing DataAware objects,
212      * sets the given DataObject to each DataAware object
213      * in the given collection
214      * @param dataAwares a collection of DataAware objects.
215      * @param dataObject new data object to set to the DataAware objects in the given collection.
216      * @param updateUI if true, calls updateUI() on each DataAware object.
setDataObject(Collection dataAwares, Object dataObject, boolean updateUI)217      */public static void setDataObject(Collection dataAwares, Object dataObject, boolean updateUI) {
218         for (Iterator i = dataAwares.iterator(); i.hasNext();)
219              ((DataAware) i.next()).setDataObject(dataObject, updateUI);
220     }
221 
222     /**
223      * Value objects read and write a value from and
224      * to an object. Typically using reflection and JavaBeans properties
225      * or directly using memeber reflection API.
226      * DataAware delegates the handling of the DataObject
227      * to a Value object.
228      * 2 implementations currently exist: PropertyValue,
229      * using JavaBeans properties reflection, and DataAwareFields classes
230      * which implement different memeber types.
231      */
232     public interface Value {
233         /**
234          * gets a value from the given object.
235          * @param target the object to get the value from.
236          * @return the value from the given object.
237          */
get(Object target)238         public Object get(Object target);
239         /**
240          * sets a value to the given object.
241          * @param value the value to set to the object.
242          * @param target the object to set the value to.
243          */
set(Object value, Object target)244         public void set(Object value, Object target);
245         /**
246          * checks if this Value object can handle
247          * the given object type as a target.
248          * @param type the type of a target to check
249          * @return true if the given class is acceptible for
250          * the Value object. False if not.
251          */
isAssignable(Class type)252         public boolean isAssignable(Class type);
253     }
254 
255     /**
256      * implementation of Value, handling JavaBeans properties through
257      * reflection.
258      * This Object gets and sets a value a specific
259      * (JavaBean-style) property on a given object.
260      * @author rp143992
261      */
262     public static class PropertyValue implements Value {
263         /**
264          * the get method of the JavaBean-style property
265          */
266         private Method getMethod;
267         /**
268          * the set method of the JavaBean-style property
269          */
270         private Method setMethod;
271 
272         /**
273          * creates a PropertyValue for the property with
274          * the given name, of the given JavaBean object.
275          * @param propertyName the property to access. Must be a Cup letter (e.g. PropertyNames.PROPERTY_NAME for getName() and setName("..."). )
276          * @param propertyOwner the object which "own" or "contains" the property.
277          */
PropertyValue(String propertyName, Object propertyOwner)278         public PropertyValue(String propertyName, Object propertyOwner) {
279             getMethod = createGetMethod(propertyName, propertyOwner);
280             setMethod = createSetMethod(propertyName, propertyOwner, getMethod.getReturnType());
281         }
282 
283         /**
284          * called from the constructor, and creates a get method reflection object
285          * for the given property and object.
286          * @param propName the property name0
287          * @param obj the object which contains the property.
288          * @return the get method reflection object.
289          */
290         private static Class[] EMPTY_ARRAY = new Class[0];
291 
createGetMethod(String propName, Object obj)292         protected Method createGetMethod(String propName, Object obj)
293         {
294             Method m = null;
295             try
296             { //try to get a "get" method.
297 
298                 m = obj.getClass().getMethod("get" + propName, EMPTY_ARRAY);
299             }
300             catch (NoSuchMethodException ex1)
301             {
302                 throw new IllegalArgumentException("get" + propName + "() method does not exist on " + obj.getClass().getName());
303             }
304             return m;
305         }
306 
307         /* (non-Javadoc)
308          * @see com.sun.star.wizards.ui.event.DataAware.Value#get(java.lang.Object)
309          */
get(Object target)310         public Object get(Object target) {
311             try {
312                 return getMethod.invoke(target, EMPTY_ARRAY);
313             } catch (IllegalAccessException ex1) {
314                 ex1.printStackTrace();
315             } catch (InvocationTargetException ex2) {
316                 ex2.printStackTrace();
317             } catch (NullPointerException npe) {
318                 if (getMethod.getReturnType().equals(String.class))
319                     return PropertyNames.EMPTY_STRING;
320                 if (getMethod.getReturnType().equals(Short.class))
321                     return new Short((short) 0);
322                 if (getMethod.getReturnType().equals(Integer.class))
323                     return 0;
324                 if (getMethod.getReturnType().equals(short[].class))
325                     return new short[0];
326             }
327             return null;
328 
329         }
330 
createSetMethod(String propName, Object obj, Class paramClass)331         protected Method createSetMethod(String propName, Object obj, Class paramClass) {
332             Method m = null;
333             try {
334                 m = obj.getClass().getMethod("set" + propName, new Class[] { paramClass });
335             } catch (NoSuchMethodException ex1) {
336                 throw new IllegalArgumentException("set" + propName + "(" + getMethod.getReturnType().getName() + ") method does not exist on " + obj.getClass().getName());
337             }
338             return m;
339         }
340 
341         /* (non-Javadoc)
342          * @see com.sun.star.wizards.ui.event.DataAware.Value#set(java.lang.Object, java.lang.Object)
343          */
set(Object value, Object target)344         public void set(Object value, Object target) {
345             try {
346                 setMethod.invoke(target, value);
347             } catch (IllegalAccessException ex1) {
348                 ex1.printStackTrace();
349             } catch (InvocationTargetException ex2) {
350                 ex2.printStackTrace();
351             }
352         }
353 
354         /* (non-Javadoc)
355          * @see com.sun.star.wizards.ui.event.DataAware.Value#isAssignable(java.lang.Class)
356          */
isAssignable(Class type)357         public boolean isAssignable(Class type) {
358             return getMethod.getDeclaringClass().isAssignableFrom(type) &&
359                 setMethod.getDeclaringClass().isAssignableFrom(type);
360         }
361     }
362 }
363