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