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