src/share/jaxws_classes/com/oracle/webservices/internal/api/message/BasePropertySet.java

Tue, 09 Apr 2013 14:51:13 +0100

author
alanb
date
Tue, 09 Apr 2013 14:51:13 +0100
changeset 368
0989ad8c0860
child 374
72e03566f0a6
permissions
-rw-r--r--

8010393: Update JAX-WS RI to 2.2.9-b12941
Reviewed-by: alanb, erikj
Contributed-by: miroslav.kos@oracle.com, martin.grebac@oracle.com

alanb@368 1 /*
alanb@368 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
alanb@368 3 *
alanb@368 4 * Copyright (c) 1997-2013 Oracle and/or its affiliates. All rights reserved.
alanb@368 5 *
alanb@368 6 * The contents of this file are subject to the terms of either the GNU
alanb@368 7 * General Public License Version 2 only ("GPL") or the Common Development
alanb@368 8 * and Distribution License("CDDL") (collectively, the "License"). You
alanb@368 9 * may not use this file except in compliance with the License. You can
alanb@368 10 * obtain a copy of the License at
alanb@368 11 * http://glassfish.java.net/public/CDDL+GPL_1_1.html
alanb@368 12 * or packager/legal/LICENSE.txt. See the License for the specific
alanb@368 13 * language governing permissions and limitations under the License.
alanb@368 14 *
alanb@368 15 * When distributing the software, include this License Header Notice in each
alanb@368 16 * file and include the License file at packager/legal/LICENSE.txt.
alanb@368 17 *
alanb@368 18 * GPL Classpath Exception:
alanb@368 19 * Oracle designates this particular file as subject to the "Classpath"
alanb@368 20 * exception as provided by Oracle in the GPL Version 2 section of the License
alanb@368 21 * file that accompanied this code.
alanb@368 22 *
alanb@368 23 * Modifications:
alanb@368 24 * If applicable, add the following below the License Header, with the fields
alanb@368 25 * enclosed by brackets [] replaced by your own identifying information:
alanb@368 26 * "Portions Copyright [year] [name of copyright owner]"
alanb@368 27 *
alanb@368 28 * Contributor(s):
alanb@368 29 * If you wish your version of this file to be governed by only the CDDL or
alanb@368 30 * only the GPL Version 2, indicate your decision by adding "[Contributor]
alanb@368 31 * elects to include this software in this distribution under the [CDDL or GPL
alanb@368 32 * Version 2] license." If you don't indicate a single choice of license, a
alanb@368 33 * recipient has the option to distribute your version of this file under
alanb@368 34 * either the CDDL, the GPL Version 2 or to extend the choice of license to
alanb@368 35 * its licensees as provided above. However, if you add GPL Version 2 code
alanb@368 36 * and therefore, elected the GPL Version 2 license, then the option applies
alanb@368 37 * only if the new code is made subject to such option by the copyright
alanb@368 38 * holder.
alanb@368 39 */
alanb@368 40
alanb@368 41 package com.oracle.webservices.internal.api.message;
alanb@368 42
alanb@368 43 import com.sun.istack.internal.NotNull;
alanb@368 44 import com.sun.istack.internal.Nullable;
alanb@368 45
alanb@368 46 import java.lang.reflect.Field;
alanb@368 47 import java.lang.reflect.InvocationTargetException;
alanb@368 48 import java.lang.reflect.Method;
alanb@368 49 import java.security.AccessController;
alanb@368 50 import java.security.PrivilegedAction;
alanb@368 51 import java.util.AbstractMap;
alanb@368 52 import java.util.HashMap;
alanb@368 53 import java.util.HashSet;
alanb@368 54 import java.util.Map;
alanb@368 55 import java.util.Map.Entry;
alanb@368 56 import java.util.Set;
alanb@368 57
alanb@368 58
alanb@368 59 /**
alanb@368 60 * A set of "properties" that can be accessed via strongly-typed fields
alanb@368 61 * as well as reflexibly through the property name.
alanb@368 62 *
alanb@368 63 * @author Kohsuke Kawaguchi
alanb@368 64 */
alanb@368 65 @SuppressWarnings("SuspiciousMethodCalls")
alanb@368 66 public abstract class BasePropertySet implements PropertySet {
alanb@368 67
alanb@368 68 /**
alanb@368 69 * Creates a new instance of TypedMap.
alanb@368 70 */
alanb@368 71 protected BasePropertySet() {
alanb@368 72 }
alanb@368 73
alanb@368 74 private Map<String,Object> mapView;
alanb@368 75
alanb@368 76 /**
alanb@368 77 * Represents the list of strongly-typed known properties
alanb@368 78 * (keyed by property names.)
alanb@368 79 *
alanb@368 80 * <p>
alanb@368 81 * Just giving it an alias to make the use of this class more fool-proof.
alanb@368 82 */
alanb@368 83 protected static class PropertyMap extends HashMap<String,Accessor> {
alanb@368 84
alanb@368 85 // the entries are often being iterated through so performance can be improved
alanb@368 86 // by their caching instead of iterating through the original (immutable) map each time
alanb@368 87 transient PropertyMapEntry[] cachedEntries = null;
alanb@368 88
alanb@368 89 PropertyMapEntry[] getPropertyMapEntries() {
alanb@368 90 if (cachedEntries == null) {
alanb@368 91 cachedEntries = createPropertyMapEntries();
alanb@368 92 }
alanb@368 93 return cachedEntries;
alanb@368 94 }
alanb@368 95
alanb@368 96 private PropertyMapEntry[] createPropertyMapEntries() {
alanb@368 97 final PropertyMapEntry[] modelEntries = new PropertyMapEntry[size()];
alanb@368 98 int i = 0;
alanb@368 99 for (final Entry<String, Accessor> e : entrySet()) {
alanb@368 100 modelEntries[i++] = new PropertyMapEntry(e.getKey(), e.getValue());
alanb@368 101 }
alanb@368 102 return modelEntries;
alanb@368 103 }
alanb@368 104
alanb@368 105 }
alanb@368 106
alanb@368 107 /**
alanb@368 108 * PropertyMapEntry represents a Map.Entry in the PropertyMap with more efficient access.
alanb@368 109 */
alanb@368 110 static public class PropertyMapEntry {
alanb@368 111 public PropertyMapEntry(String k, Accessor v) {
alanb@368 112 key = k; value = v;
alanb@368 113 }
alanb@368 114 String key;
alanb@368 115 Accessor value;
alanb@368 116 }
alanb@368 117
alanb@368 118 /**
alanb@368 119 * Map representing the Fields and Methods annotated with {@link PropertySet.Property}.
alanb@368 120 * Model of {@link PropertySet} class.
alanb@368 121 *
alanb@368 122 * <p>
alanb@368 123 * At the end of the derivation chain this method just needs to be implemented
alanb@368 124 * as:
alanb@368 125 *
alanb@368 126 * <pre>
alanb@368 127 * private static final PropertyMap model;
alanb@368 128 * static {
alanb@368 129 * model = parse(MyDerivedClass.class);
alanb@368 130 * }
alanb@368 131 * protected PropertyMap getPropertyMap() {
alanb@368 132 * return model;
alanb@368 133 * }
alanb@368 134 * </pre>
alanb@368 135 */
alanb@368 136 protected abstract PropertyMap getPropertyMap();
alanb@368 137
alanb@368 138 /**
alanb@368 139 * This method parses a class for fields and methods with {@link PropertySet.Property}.
alanb@368 140 */
alanb@368 141 protected static PropertyMap parse(final Class clazz) {
alanb@368 142 // make all relevant fields and methods accessible.
alanb@368 143 // this allows runtime to skip the security check, so they runs faster.
alanb@368 144 return AccessController.doPrivileged(new PrivilegedAction<PropertyMap>() {
alanb@368 145 @Override
alanb@368 146 public PropertyMap run() {
alanb@368 147 PropertyMap props = new PropertyMap();
alanb@368 148 for (Class c=clazz; c!=null; c=c.getSuperclass()) {
alanb@368 149 for (Field f : c.getDeclaredFields()) {
alanb@368 150 Property cp = f.getAnnotation(Property.class);
alanb@368 151 if(cp!=null) {
alanb@368 152 for(String value : cp.value()) {
alanb@368 153 props.put(value, new FieldAccessor(f, value));
alanb@368 154 }
alanb@368 155 }
alanb@368 156 }
alanb@368 157 for (Method m : c.getDeclaredMethods()) {
alanb@368 158 Property cp = m.getAnnotation(Property.class);
alanb@368 159 if(cp!=null) {
alanb@368 160 String name = m.getName();
alanb@368 161 assert name.startsWith("get") || name.startsWith("is");
alanb@368 162
alanb@368 163 String setName = name.startsWith("is") ? "set"+name.substring(2) : // isFoo -> setFoo
alanb@368 164 's' +name.substring(1); // getFoo -> setFoo
alanb@368 165 Method setter;
alanb@368 166 try {
alanb@368 167 setter = clazz.getMethod(setName,m.getReturnType());
alanb@368 168 } catch (NoSuchMethodException e) {
alanb@368 169 setter = null; // no setter
alanb@368 170 }
alanb@368 171 for(String value : cp.value()) {
alanb@368 172 props.put(value, new MethodAccessor(m, setter, value));
alanb@368 173 }
alanb@368 174 }
alanb@368 175 }
alanb@368 176 }
alanb@368 177
alanb@368 178 return props;
alanb@368 179 }
alanb@368 180 });
alanb@368 181 }
alanb@368 182
alanb@368 183 /**
alanb@368 184 * Represents a typed property defined on a {@link PropertySet}.
alanb@368 185 */
alanb@368 186 protected interface Accessor {
alanb@368 187 String getName();
alanb@368 188 boolean hasValue(PropertySet props);
alanb@368 189 Object get(PropertySet props);
alanb@368 190 void set(PropertySet props, Object value);
alanb@368 191 }
alanb@368 192
alanb@368 193 static final class FieldAccessor implements Accessor {
alanb@368 194 /**
alanb@368 195 * Field with the annotation.
alanb@368 196 */
alanb@368 197 private final Field f;
alanb@368 198
alanb@368 199 /**
alanb@368 200 * One of the values in {@link Property} annotation on {@link #f}.
alanb@368 201 */
alanb@368 202 private final String name;
alanb@368 203
alanb@368 204 protected FieldAccessor(Field f, String name) {
alanb@368 205 this.f = f;
alanb@368 206 f.setAccessible(true);
alanb@368 207 this.name = name;
alanb@368 208 }
alanb@368 209
alanb@368 210 @Override
alanb@368 211 public String getName() {
alanb@368 212 return name;
alanb@368 213 }
alanb@368 214
alanb@368 215 @Override
alanb@368 216 public boolean hasValue(PropertySet props) {
alanb@368 217 return get(props)!=null;
alanb@368 218 }
alanb@368 219
alanb@368 220 @Override
alanb@368 221 public Object get(PropertySet props) {
alanb@368 222 try {
alanb@368 223 return f.get(props);
alanb@368 224 } catch (IllegalAccessException e) {
alanb@368 225 throw new AssertionError();
alanb@368 226 }
alanb@368 227 }
alanb@368 228
alanb@368 229 @Override
alanb@368 230 public void set(PropertySet props, Object value) {
alanb@368 231 try {
alanb@368 232 f.set(props,value);
alanb@368 233 } catch (IllegalAccessException e) {
alanb@368 234 throw new AssertionError();
alanb@368 235 }
alanb@368 236 }
alanb@368 237 }
alanb@368 238
alanb@368 239 static final class MethodAccessor implements Accessor {
alanb@368 240 /**
alanb@368 241 * Getter method.
alanb@368 242 */
alanb@368 243 private final @NotNull Method getter;
alanb@368 244 /**
alanb@368 245 * Setter method.
alanb@368 246 * Some property is read-only.
alanb@368 247 */
alanb@368 248 private final @Nullable Method setter;
alanb@368 249
alanb@368 250 /**
alanb@368 251 * One of the values in {@link Property} annotation on {@link #getter}.
alanb@368 252 */
alanb@368 253 private final String name;
alanb@368 254
alanb@368 255 protected MethodAccessor(Method getter, Method setter, String value) {
alanb@368 256 this.getter = getter;
alanb@368 257 this.setter = setter;
alanb@368 258 this.name = value;
alanb@368 259 getter.setAccessible(true);
alanb@368 260 if (setter!=null) {
alanb@368 261 setter.setAccessible(true);
alanb@368 262 }
alanb@368 263 }
alanb@368 264
alanb@368 265 @Override
alanb@368 266 public String getName() {
alanb@368 267 return name;
alanb@368 268 }
alanb@368 269
alanb@368 270 @Override
alanb@368 271 public boolean hasValue(PropertySet props) {
alanb@368 272 return get(props)!=null;
alanb@368 273 }
alanb@368 274
alanb@368 275 @Override
alanb@368 276 public Object get(PropertySet props) {
alanb@368 277 try {
alanb@368 278 return getter.invoke(props);
alanb@368 279 } catch (IllegalAccessException e) {
alanb@368 280 throw new AssertionError();
alanb@368 281 } catch (InvocationTargetException e) {
alanb@368 282 handle(e);
alanb@368 283 return 0; // never reach here
alanb@368 284 }
alanb@368 285 }
alanb@368 286
alanb@368 287 @Override
alanb@368 288 public void set(PropertySet props, Object value) {
alanb@368 289 if(setter==null) {
alanb@368 290 throw new ReadOnlyPropertyException(getName());
alanb@368 291 }
alanb@368 292 try {
alanb@368 293 setter.invoke(props,value);
alanb@368 294 } catch (IllegalAccessException e) {
alanb@368 295 throw new AssertionError();
alanb@368 296 } catch (InvocationTargetException e) {
alanb@368 297 handle(e);
alanb@368 298 }
alanb@368 299 }
alanb@368 300
alanb@368 301 /**
alanb@368 302 * Since we don't expect the getter/setter to throw a checked exception,
alanb@368 303 * it should be possible to make the exception propagation transparent.
alanb@368 304 * That's what we are trying to do here.
alanb@368 305 */
alanb@368 306 private Exception handle(InvocationTargetException e) {
alanb@368 307 Throwable t = e.getTargetException();
alanb@368 308 if (t instanceof Error) {
alanb@368 309 throw (Error)t;
alanb@368 310 }
alanb@368 311 if (t instanceof RuntimeException) {
alanb@368 312 throw (RuntimeException)t;
alanb@368 313 }
alanb@368 314 throw new Error(e);
alanb@368 315 }
alanb@368 316 }
alanb@368 317
alanb@368 318
alanb@368 319 /**
alanb@368 320 * Class allowing to work with PropertySet object as with a Map; it doesn't only allow to read properties from
alanb@368 321 * the map but also to modify the map in a way it is in sync with original strongly typed fields. It also allows
alanb@368 322 * (if necessary) to store additional properties those can't be found in strongly typed fields.
alanb@368 323 *
alanb@368 324 * @see com.sun.xml.internal.ws.api.PropertySet#asMap() method
alanb@368 325 */
alanb@368 326 final class MapView extends HashMap<String, Object> {
alanb@368 327
alanb@368 328 // flag if it should allow store also different properties
alanb@368 329 // than the from strongly typed fields
alanb@368 330 boolean extensible;
alanb@368 331
alanb@368 332 MapView(boolean extensible) {
alanb@368 333 super(getPropertyMap().getPropertyMapEntries().length);
alanb@368 334 this.extensible = extensible;
alanb@368 335 initialize();
alanb@368 336 }
alanb@368 337
alanb@368 338 public void initialize() {
alanb@368 339 // iterate (cached) array instead of map to speed things up ...
alanb@368 340 PropertyMapEntry[] entries = getPropertyMap().getPropertyMapEntries();
alanb@368 341 for (PropertyMapEntry entry : entries) {
alanb@368 342 super.put(entry.key, entry.value);
alanb@368 343 }
alanb@368 344 }
alanb@368 345
alanb@368 346 @Override
alanb@368 347 public Object get(Object key) {
alanb@368 348 Object o = super.get(key);
alanb@368 349 if (o instanceof Accessor) {
alanb@368 350 return ((Accessor) o).get(BasePropertySet.this);
alanb@368 351 } else {
alanb@368 352 return o;
alanb@368 353 }
alanb@368 354 }
alanb@368 355
alanb@368 356 @Override
alanb@368 357 public Set<Entry<String, Object>> entrySet() {
alanb@368 358 Set<Entry<String, Object>> entries = new HashSet<Entry<String, Object>>();
alanb@368 359 for (String key : keySet()) {
alanb@368 360 entries.add(new SimpleImmutableEntry<String, Object>(key, get(key)));
alanb@368 361 }
alanb@368 362 return entries;
alanb@368 363 }
alanb@368 364
alanb@368 365 @Override
alanb@368 366 public Object put(String key, Object value) {
alanb@368 367
alanb@368 368 Object o = super.get(key);
alanb@368 369 if (o != null && o instanceof Accessor) {
alanb@368 370
alanb@368 371 Object oldValue = ((Accessor) o).get(BasePropertySet.this);
alanb@368 372 ((Accessor) o).set(BasePropertySet.this, value);
alanb@368 373 return oldValue;
alanb@368 374
alanb@368 375 } else {
alanb@368 376
alanb@368 377 if (extensible) {
alanb@368 378 return super.put(key, value);
alanb@368 379 } else {
alanb@368 380 throw new IllegalStateException("Unknown property [" + key + "] for PropertySet [" +
alanb@368 381 BasePropertySet.this.getClass().getName() + "]");
alanb@368 382 }
alanb@368 383 }
alanb@368 384 }
alanb@368 385
alanb@368 386 @Override
alanb@368 387 public void clear() {
alanb@368 388 for (String key : keySet()) {
alanb@368 389 remove(key);
alanb@368 390 }
alanb@368 391 }
alanb@368 392
alanb@368 393 @Override
alanb@368 394 public Object remove(Object key) {
alanb@368 395 Object o;
alanb@368 396 o = super.get(key);
alanb@368 397 if (o instanceof Accessor) {
alanb@368 398 ((Accessor)o).set(BasePropertySet.this, null);
alanb@368 399 }
alanb@368 400 return super.remove(key);
alanb@368 401 }
alanb@368 402 }
alanb@368 403
alanb@368 404 @Override
alanb@368 405 public boolean containsKey(Object key) {
alanb@368 406 Accessor sp = getPropertyMap().get(key);
alanb@368 407 if (sp != null) {
alanb@368 408 return sp.get(this) != null;
alanb@368 409 }
alanb@368 410 return false;
alanb@368 411 }
alanb@368 412
alanb@368 413 /**
alanb@368 414 * Gets the name of the property.
alanb@368 415 *
alanb@368 416 * @param key
alanb@368 417 * This field is typed as {@link Object} to follow the {@link Map#get(Object)}
alanb@368 418 * convention, but if anything but {@link String} is passed, this method
alanb@368 419 * just returns null.
alanb@368 420 */
alanb@368 421 @Override
alanb@368 422 public Object get(Object key) {
alanb@368 423 Accessor sp = getPropertyMap().get(key);
alanb@368 424 if (sp != null) {
alanb@368 425 return sp.get(this);
alanb@368 426 }
alanb@368 427 throw new IllegalArgumentException("Undefined property "+key);
alanb@368 428 }
alanb@368 429
alanb@368 430 /**
alanb@368 431 * Sets a property.
alanb@368 432 *
alanb@368 433 * <h3>Implementation Note</h3>
alanb@368 434 * This method is slow. Code inside JAX-WS should define strongly-typed
alanb@368 435 * fields in this class and access them directly, instead of using this.
alanb@368 436 *
alanb@368 437 * @throws ReadOnlyPropertyException
alanb@368 438 * if the given key is an alias of a strongly-typed field,
alanb@368 439 * and if the name object given is not assignable to the field.
alanb@368 440 *
alanb@368 441 * @see Property
alanb@368 442 */
alanb@368 443 @Override
alanb@368 444 public Object put(String key, Object value) {
alanb@368 445 Accessor sp = getPropertyMap().get(key);
alanb@368 446 if(sp!=null) {
alanb@368 447 Object old = sp.get(this);
alanb@368 448 sp.set(this,value);
alanb@368 449 return old;
alanb@368 450 } else {
alanb@368 451 throw new IllegalArgumentException("Undefined property "+key);
alanb@368 452 }
alanb@368 453 }
alanb@368 454
alanb@368 455 /**
alanb@368 456 * Checks if this {@link PropertySet} supports a property of the given name.
alanb@368 457 */
alanb@368 458 @Override
alanb@368 459 public boolean supports(Object key) {
alanb@368 460 return getPropertyMap().containsKey(key);
alanb@368 461 }
alanb@368 462
alanb@368 463 @Override
alanb@368 464 public Object remove(Object key) {
alanb@368 465 Accessor sp = getPropertyMap().get(key);
alanb@368 466 if(sp!=null) {
alanb@368 467 Object old = sp.get(this);
alanb@368 468 sp.set(this,null);
alanb@368 469 return old;
alanb@368 470 } else {
alanb@368 471 throw new IllegalArgumentException("Undefined property "+key);
alanb@368 472 }
alanb@368 473 }
alanb@368 474
alanb@368 475 /**
alanb@368 476 * Creates a {@link Map} view of this {@link PropertySet}.
alanb@368 477 *
alanb@368 478 * <p>
alanb@368 479 * This map is partially live, in the sense that values you set to it
alanb@368 480 * will be reflected to {@link PropertySet}.
alanb@368 481 *
alanb@368 482 * <p>
alanb@368 483 * However, this map may not pick up changes made
alanb@368 484 * to {@link PropertySet} after the view is created.
alanb@368 485 *
alanb@368 486 * @deprecated use newer implementation {@link PropertySet#asMap()} which produces
alanb@368 487 * readwrite {@link Map}
alanb@368 488 *
alanb@368 489 * @return
alanb@368 490 * always non-null valid instance.
alanb@368 491 */
alanb@368 492 @Deprecated
alanb@368 493 @Override
alanb@368 494 public final Map<String,Object> createMapView() {
alanb@368 495 final Set<Entry<String,Object>> core = new HashSet<Entry<String,Object>>();
alanb@368 496 createEntrySet(core);
alanb@368 497
alanb@368 498 return new AbstractMap<String, Object>() {
alanb@368 499 @Override
alanb@368 500 public Set<Entry<String,Object>> entrySet() {
alanb@368 501 return core;
alanb@368 502 }
alanb@368 503 };
alanb@368 504 }
alanb@368 505
alanb@368 506 /**
alanb@368 507 * Creates a modifiable {@link Map} view of this {@link PropertySet}.
alanb@368 508 * <p/>
alanb@368 509 * Changes done on this {@link Map} or on {@link PropertySet} object work in both directions - values made to
alanb@368 510 * {@link Map} are reflected to {@link PropertySet} and changes done using getters/setters on {@link PropertySet}
alanb@368 511 * object are automatically reflected in this {@link Map}.
alanb@368 512 * <p/>
alanb@368 513 * If necessary, it also can hold other values (not present on {@link PropertySet}) -
alanb@368 514 * {@see PropertySet#mapAllowsAdditionalProperties}
alanb@368 515 *
alanb@368 516 * @return always non-null valid instance.
alanb@368 517 */
alanb@368 518 @Override
alanb@368 519 public Map<String, Object> asMap() {
alanb@368 520 if (mapView == null) {
alanb@368 521 mapView = createView();
alanb@368 522 }
alanb@368 523 return mapView;
alanb@368 524 }
alanb@368 525
alanb@368 526 protected Map<String, Object> createView() {
alanb@368 527 return new MapView(mapAllowsAdditionalProperties());
alanb@368 528 }
alanb@368 529
alanb@368 530 /**
alanb@368 531 * Used when constructing the {@link MapView} for this object - it controls if the {@link MapView} servers only to
alanb@368 532 * access strongly typed values or allows also different values
alanb@368 533 *
alanb@368 534 * @return true if {@link Map} should allow also properties not defined as strongly typed fields
alanb@368 535 */
alanb@368 536 protected boolean mapAllowsAdditionalProperties() {
alanb@368 537 return false;
alanb@368 538 }
alanb@368 539
alanb@368 540 protected void createEntrySet(Set<Entry<String,Object>> core) {
alanb@368 541 for (final Entry<String, Accessor> e : getPropertyMap().entrySet()) {
alanb@368 542 core.add(new Entry<String, Object>() {
alanb@368 543 @Override
alanb@368 544 public String getKey() {
alanb@368 545 return e.getKey();
alanb@368 546 }
alanb@368 547
alanb@368 548 @Override
alanb@368 549 public Object getValue() {
alanb@368 550 return e.getValue().get(BasePropertySet.this);
alanb@368 551 }
alanb@368 552
alanb@368 553 @Override
alanb@368 554 public Object setValue(Object value) {
alanb@368 555 Accessor acc = e.getValue();
alanb@368 556 Object old = acc.get(BasePropertySet.this);
alanb@368 557 acc.set(BasePropertySet.this,value);
alanb@368 558 return old;
alanb@368 559 }
alanb@368 560 });
alanb@368 561 }
alanb@368 562 }
alanb@368 563 }

mercurial