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

Tue, 23 Apr 2013 18:33:20 -0700

author
katleman
date
Tue, 23 Apr 2013 18:33:20 -0700
changeset 374
72e03566f0a6
parent 368
0989ad8c0860
child 637
9c07ef4934dd
permissions
-rw-r--r--

8012643: JDK8 b86 source with GPL header errors
Reviewed-by: dholmes, alanb

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

mercurial