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 org.apache.openoffice.comp.sdbc.dbtools.comphelper; 23 24 import java.util.Arrays; 25 import java.util.Comparator; 26 import java.util.HashMap; 27 import java.util.Iterator; 28 import java.util.Map; 29 import java.util.concurrent.atomic.AtomicInteger; 30 31 import com.sun.star.beans.Property; 32 import com.sun.star.beans.PropertyAttribute; 33 import com.sun.star.beans.PropertyChangeEvent; 34 import com.sun.star.beans.PropertyVetoException; 35 import com.sun.star.beans.UnknownPropertyException; 36 import com.sun.star.beans.XFastPropertySet; 37 import com.sun.star.beans.XMultiPropertySet; 38 import com.sun.star.beans.XPropertiesChangeListener; 39 import com.sun.star.beans.XPropertyChangeListener; 40 import com.sun.star.beans.XPropertySet; 41 import com.sun.star.beans.XPropertySetInfo; 42 import com.sun.star.beans.XVetoableChangeListener; 43 import com.sun.star.lang.EventObject; 44 import com.sun.star.lang.IllegalArgumentException; 45 import com.sun.star.lang.WrappedTargetException; 46 import com.sun.star.lib.uno.helper.InterfaceContainer; 47 import com.sun.star.lib.uno.helper.MultiTypeInterfaceContainer; 48 import com.sun.star.uno.Any; 49 import com.sun.star.uno.AnyConverter; 50 import com.sun.star.uno.Type; 51 import com.sun.star.uno.TypeClass; 52 import com.sun.star.uno.XInterface; 53 54 public class PropertySetAdapter implements XPropertySet, XFastPropertySet, XMultiPropertySet { 55 private final Object lock; 56 private final Object eventSource; 57 // after registerListeners(), these are read-only: 58 private final Map<String,PropertyData> propertiesByName = new HashMap<String,PropertyData>(); 59 private final Map<Integer,PropertyData> propertiesByHandle = new HashMap<Integer,PropertyData>(); 60 private AtomicInteger nextHandle = new AtomicInteger(1); 61 // interface containers are locked internally: 62 protected final MultiTypeInterfaceContainer boundListeners = new MultiTypeInterfaceContainer(); 63 protected final MultiTypeInterfaceContainer vetoableListeners = new MultiTypeInterfaceContainer(); 64 protected final InterfaceContainer propertiesChangeListeners = new InterfaceContainer(); 65 private final PropertySetInfo propertySetInfo = new PropertySetInfo(); 66 67 public static interface PropertyGetter { getValue()68 Object getValue() throws WrappedTargetException; 69 } 70 71 public static interface PropertySetter { setValue(Object value)72 void setValue(Object value) throws PropertyVetoException, IllegalArgumentException, WrappedTargetException; 73 } 74 75 private static class PropertyData { 76 Property property; 77 PropertyGetter getter; 78 PropertySetter setter; 79 PropertyData(Property property, PropertyGetter getter, PropertySetter setter)80 PropertyData(Property property, PropertyGetter getter, PropertySetter setter) { 81 this.property = property; 82 this.getter = getter; 83 this.setter = setter; 84 } 85 } 86 87 private static final Comparator<Property> propertyNameComparator = new Comparator<Property>() { 88 @Override 89 public int compare(Property first, Property second) { 90 return first.Name.compareTo(second.Name); 91 } 92 }; 93 94 private class PropertySetInfo implements XPropertySetInfo { 95 @Override getProperties()96 public Property[] getProperties() { 97 Property[] properties = new Property[propertiesByName.size()]; 98 int next = 0; 99 for (Map.Entry<String,PropertyData> entry : propertiesByName.entrySet()) { 100 properties[next++] = entry.getValue().property; 101 } 102 Arrays.sort(properties, propertyNameComparator); 103 return properties; 104 } 105 106 @Override getPropertyByName(String propertyName)107 public Property getPropertyByName(String propertyName) throws UnknownPropertyException { 108 PropertyData propertyData = getPropertyData(propertyName); 109 return propertyData.property; 110 } 111 112 @Override hasPropertyByName(String propertyName)113 public boolean hasPropertyByName(String propertyName) { 114 return propertiesByName.containsKey(propertyName); 115 } 116 } 117 118 /** 119 * Creates a new instance. 120 * @param lock the lock that will be held while calling the getters and setters 121 * @param eventSource the com.sun.star.lang.EventObject Source field, to use in events sent to listeners 122 */ PropertySetAdapter(Object lock, Object eventSource)123 public PropertySetAdapter(Object lock, Object eventSource) { 124 this.lock = lock; 125 this.eventSource = eventSource; 126 } 127 dispose()128 public void dispose() { 129 // Create an event with this as sender 130 EventObject event = new EventObject(eventSource); 131 132 // inform all listeners to release this object 133 boundListeners.disposeAndClear(event); 134 vetoableListeners.disposeAndClear(event); 135 } 136 registerProperty(String propertyName, int handle, Type type, short attributes, PropertyGetter getter, PropertySetter setter)137 public void registerProperty(String propertyName, int handle, Type type, short attributes, 138 PropertyGetter getter, PropertySetter setter) { 139 Property property = new Property(propertyName, handle, type, attributes); 140 PropertyData propertyData = new PropertyData(property, getter, setter); 141 propertiesByName.put(propertyName, propertyData); 142 propertiesByHandle.put(property.Handle, propertyData); 143 } 144 registerProperty(String propertyName, Type type, short attributes, PropertyGetter getter, PropertySetter setter)145 public void registerProperty(String propertyName, Type type, short attributes, 146 PropertyGetter getter, PropertySetter setter) { 147 int handle; 148 // registerProperty() should only be called from one thread, but just in case: 149 handle = nextHandle.getAndIncrement(); 150 registerProperty(propertyName, handle, type, attributes, getter, setter); 151 } 152 153 @Override addPropertyChangeListener( String propertyName, XPropertyChangeListener listener)154 public void addPropertyChangeListener( 155 String propertyName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException { 156 PropertyData propertyData = getPropertyData(propertyName); 157 if ((propertyData.property.Attributes & PropertyAttribute.BOUND) != 0) { 158 boundListeners.addInterface(propertyName, listener); 159 } // else ignore silently 160 } 161 162 @Override addVetoableChangeListener( String propertyName, XVetoableChangeListener listener)163 public void addVetoableChangeListener( 164 String propertyName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException { 165 PropertyData propertyData = getPropertyData(propertyName); 166 if ((propertyData.property.Attributes & PropertyAttribute.CONSTRAINED) != 0) { 167 vetoableListeners.addInterface(propertyName, listener); 168 } // else ignore silently 169 } 170 171 @Override addPropertiesChangeListener(String[] propertyNames, XPropertiesChangeListener listener)172 public void addPropertiesChangeListener(String[] propertyNames, XPropertiesChangeListener listener) { 173 propertiesChangeListeners.add(listener); 174 } 175 176 @Override getPropertySetInfo()177 public XPropertySetInfo getPropertySetInfo() { 178 return propertySetInfo; 179 } 180 getPropertyData(String propertyName)181 private PropertyData getPropertyData(String propertyName) throws UnknownPropertyException { 182 PropertyData propertyData = propertiesByName.get(propertyName); 183 if (propertyData == null) { 184 throw new UnknownPropertyException(propertyName); 185 } 186 return propertyData; 187 } 188 getPropertyData(int handle)189 private PropertyData getPropertyData(int handle) throws UnknownPropertyException { 190 PropertyData propertyData = propertiesByHandle.get(handle); 191 if (propertyData == null) { 192 throw new UnknownPropertyException(Integer.toString(handle)); 193 } 194 return propertyData; 195 } 196 getPropertyValue(PropertyData propertyData)197 private Object getPropertyValue(PropertyData propertyData) throws WrappedTargetException { 198 Object ret; 199 synchronized (lock) { 200 ret = propertyData.getter.getValue(); 201 } 202 203 // null must not be returned. Either a void any is returned or an any containing 204 // an interface type and a null reference. 205 if (ret == null) { 206 if (propertyData.property.Type.getTypeClass() == TypeClass.INTERFACE) { 207 ret = new Any(propertyData.property.Type, null); 208 } else { 209 ret = new Any(new Type(void.class), null); 210 } 211 } 212 return ret; 213 } 214 215 @Override getPropertyValue(String propertyName)216 public Object getPropertyValue(String propertyName) throws UnknownPropertyException, WrappedTargetException { 217 PropertyData propertyData = getPropertyData(propertyName); 218 return getPropertyValue(propertyData); 219 } 220 221 @Override getFastPropertyValue(int handle)222 public Object getFastPropertyValue(int handle) throws UnknownPropertyException, WrappedTargetException { 223 PropertyData propertyData = getPropertyData(handle); 224 return getPropertyValue(propertyData); 225 } 226 227 @Override getPropertyValues(String[] propertyNames)228 public Object[] getPropertyValues(String[] propertyNames) { 229 Object[] values = new Object[propertyNames.length]; 230 for (int i = 0; i < propertyNames.length; i++) { 231 Object value = null; 232 try { 233 value = getPropertyValue(propertyNames[i]); 234 } catch (UnknownPropertyException unknownPropertyException) { 235 } catch (WrappedTargetException wrappedTargetException) { 236 } 237 values[i] = value; 238 } 239 return values; 240 } 241 242 @Override removePropertyChangeListener( String propertyName, XPropertyChangeListener listener)243 public void removePropertyChangeListener( 244 String propertyName, XPropertyChangeListener listener) throws UnknownPropertyException, WrappedTargetException { 245 // check existence: 246 getPropertyData(propertyName); 247 boundListeners.removeInterface(propertyName, listener); 248 } 249 250 @Override removeVetoableChangeListener( String propertyName, XVetoableChangeListener listener)251 public synchronized void removeVetoableChangeListener( 252 String propertyName, XVetoableChangeListener listener) throws UnknownPropertyException, WrappedTargetException { 253 // check existence: 254 getPropertyData(propertyName); 255 vetoableListeners.removeInterface(propertyName, listener); 256 } 257 258 @Override removePropertiesChangeListener(XPropertiesChangeListener listener)259 public void removePropertiesChangeListener(XPropertiesChangeListener listener) { 260 propertiesChangeListeners.remove(listener); 261 } 262 263 @Override setPropertyValue(String propertyName, Object value)264 public void setPropertyValue(String propertyName, Object value) 265 throws UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException { 266 PropertyData propertyData = getPropertyData(propertyName); 267 setPropertyValue(propertyData, value); 268 } 269 270 @Override setFastPropertyValue(int handle, Object value)271 public void setFastPropertyValue(int handle, Object value) 272 throws UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException { 273 PropertyData propertyData = getPropertyData(handle); 274 setPropertyValue(propertyData, value); 275 } 276 setPropertyValue(PropertyData propertyData, Object value)277 private void setPropertyValue(PropertyData propertyData, Object value) 278 throws UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException { 279 if ((propertyData.property.Attributes & PropertyAttribute.READONLY) != 0) { 280 throw new PropertyVetoException(); 281 } 282 // The value may be null only if MAYBEVOID attribute is set 283 boolean isVoid = false; 284 if (value instanceof Any) { 285 isVoid = ((Any) value).getObject() == null; 286 } else { 287 isVoid = value == null; 288 } 289 if (isVoid && (propertyData.property.Attributes & PropertyAttribute.MAYBEVOID) == 0) { 290 throw new IllegalArgumentException("The property must have a value; the MAYBEVOID attribute is not set!"); 291 } 292 293 // Check if the argument is allowed 294 boolean isValueOk = false; 295 if (value instanceof Any) { 296 isValueOk = checkType(((Any) value).getObject()); 297 } else { 298 isValueOk = checkType(value); 299 } 300 if (!isValueOk) { 301 throw new IllegalArgumentException("No valid UNO type"); 302 } 303 304 Object[] futureValue = new Object[] { AnyConverter.toObject(propertyData.property.Type, value) }; 305 Object[] currentValue = new Object[] { getPropertyValue(propertyData.property.Name) }; 306 Property[] properties = new Property[] { propertyData.property }; 307 308 fire(properties, currentValue, futureValue, false); 309 synchronized (lock) { 310 propertyData.setter.setValue(futureValue[0]); 311 } 312 fire(properties, currentValue, futureValue, true); 313 } 314 315 @Override setPropertyValues(String[] propertyNames, Object[] values)316 public void setPropertyValues(String[] propertyNames, Object[] values) throws PropertyVetoException, IllegalArgumentException, WrappedTargetException { 317 for (int i = 0; i < propertyNames.length; i++) { 318 try { 319 setPropertyValue(propertyNames[i], values[i]); 320 } catch (UnknownPropertyException e) { 321 continue; 322 } 323 } 324 } 325 checkType(Object obj)326 private boolean checkType(Object obj) { 327 if (obj == null 328 || obj instanceof Boolean 329 || obj instanceof Character 330 || obj instanceof Number 331 || obj instanceof String 332 || obj instanceof XInterface 333 || obj instanceof Type 334 || obj instanceof com.sun.star.uno.Enum 335 || obj.getClass().isArray()) 336 return true; 337 return false; 338 } 339 340 @Override firePropertiesChangeEvent(String[] propertyNames, XPropertiesChangeListener listener)341 public void firePropertiesChangeEvent(String[] propertyNames, XPropertiesChangeListener listener) { 342 PropertyChangeEvent[] events = new PropertyChangeEvent[propertyNames.length]; 343 int eventCount = 0; 344 for (int i = 0; i < propertyNames.length; i++) { 345 try { 346 PropertyData propertyData = getPropertyData(propertyNames[i]); 347 Object value = getPropertyValue(propertyNames[i]); 348 events[eventCount++] = new PropertyChangeEvent(eventSource, propertyNames[i], 349 false, propertyData.property.Handle, value, value); 350 } catch (UnknownPropertyException unknownPropertyException) { 351 } catch (WrappedTargetException wrappedTargetException) { 352 } 353 } 354 if (eventCount > 0) { 355 if (events.length != eventCount) { 356 PropertyChangeEvent[] tmp = new PropertyChangeEvent[eventCount]; 357 System.arraycopy(events, 0, tmp, 0, eventCount); 358 events = tmp; 359 } 360 listener.propertiesChange(events); 361 } 362 } 363 fire(Property[] properties, Object[] oldValues, Object[] newValues, boolean hasChanged)364 private void fire(Property[] properties, Object[] oldValues, Object[] newValues, boolean hasChanged) throws PropertyVetoException { 365 PropertyChangeEvent[] events = new PropertyChangeEvent[properties.length]; 366 int eventCount = 0; 367 for (int i = 0; i < properties.length; i++) { 368 if ((!hasChanged && (properties[i].Attributes & PropertyAttribute.CONSTRAINED) != 0) || 369 (hasChanged && (properties[i].Attributes & PropertyAttribute.BOUND) != 0)) { 370 events[eventCount++] = new PropertyChangeEvent( 371 eventSource, properties[i].Name, false, properties[i].Handle, oldValues[i], newValues[i]); 372 } 373 } 374 for (int i = 0; i < eventCount; i++) { 375 fireListeners(hasChanged, events[i].PropertyName, events[i]); 376 fireListeners(hasChanged, "", events[i]); 377 } 378 if (hasChanged && eventCount > 0) { 379 if (eventCount != events.length) { 380 PropertyChangeEvent[] tmp = new PropertyChangeEvent[eventCount]; 381 System.arraycopy(events, 0, tmp, 0, eventCount); 382 events = tmp; 383 } 384 for (Iterator<?> it = propertiesChangeListeners.iterator(); it.hasNext();) { 385 XPropertiesChangeListener listener = (XPropertiesChangeListener) it.next(); 386 listener.propertiesChange(events); 387 } 388 } 389 } 390 fireListeners(boolean hasChanged, String key, PropertyChangeEvent event)391 private void fireListeners(boolean hasChanged, String key, PropertyChangeEvent event) throws PropertyVetoException { 392 InterfaceContainer listeners; 393 if (hasChanged) { 394 listeners = boundListeners.getContainer(key); 395 } else { 396 listeners = vetoableListeners.getContainer(key); 397 } 398 if (listeners != null) { 399 Iterator<?> it = listeners.iterator(); 400 while (it.hasNext()) { 401 Object listener = it.next(); 402 if (hasChanged) { 403 ((XPropertyChangeListener)listener).propertyChange(event); 404 } else { 405 ((XVetoableChangeListener)listener).vetoableChange(event); 406 } 407 } 408 } 409 } 410 } 411