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