001/* 002 * file PropertyRequestItem.java 003 * 004 * Licensed Materials - Property of IBM 005 * Restricted Materials of IBM 006 * 007 * (c) Copyright IBM Corporation 2004, 2008. All Rights Reserved. 008 * Note to U.S. Government Users Restricted Rights: Use, duplication or 009 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 010 */ 011package javax.wvcm; 012 013import java.text.MessageFormat; 014import java.util.Collections; 015import java.util.HashMap; 016import java.util.Iterator; 017import java.util.Map; 018import java.util.Map.Entry; 019 020import javax.wvcm.PropertyNameList.PropertyName; 021 022 023/** 024 * A specification of the types of objects can be used to create a PropertyRequest. 025 * 026 * @since 1.0 027 */ 028public interface PropertyRequestItem { 029 030 /** 031 * A map of PropertyName to PropertyRequest objects, used to specify one or more 032 * properties whose values are to be read from a resource. 033 * <p> 034 * If the property value is itself a resource (or a data structure containing resources), 035 * then the PropertyRequest value of the entry for the PropertyName of that property 036 * specifies the properties that should be retrieved for that resource. 037 * Such a request can be constructed using the {@link PropertyName#nest} method. 038 * <p> 039 * For example, the following code fragment creates a PropertyRequest that 040 * specifies a request for the {@link Resource#CREATOR_DISPLAY_NAME}, 041 * {@link ControllableResource#CHECKED_IN}, and 042 * {@link Resource#LAST_MODIFIED} properties, as well as the 043 * {@link Version#VERSION_NAME} and {@link Resource#CREATION_DATE} 044 * properties of the resource that is the value of the 045 * {@link ControllableResource#CHECKED_IN} property: 046 * 047 * <pre> 048 * PropertyRequest props = new PropertyRequest( 049 * Resource.CREATOR_DISPLAY_NAME, 050 * ControllableResource.CHECKED_IN.nest( 051 * Version.VERSION_NAME, 052 * Resource.CREATION_DATE), 053 * Resource.LAST_MODIFIED); 054 * </pre> 055 */ 056 public class PropertyRequest implements PropertyRequestItem, Feedback 057 { 058 059 private Map<PropertyName<?>, PropertyRequest> _itemMap; 060 061 /** 062 * A cached array form of the property request. 063 */ 064 private NestedPropertyName<?>[] _itemArray; 065 066 private static final NestedPropertyName<?>[] EMPTY_PNA = 067 new NestedPropertyName[0]; 068 069 /** 070 * An empty PropertyRequest. 071 */ 072 public static final PropertyRequest EMPTY = 073 new PropertyRequest(); 074 075 private static final Map<PropertyName<?>, PropertyRequest> EMPTY_MAP = 076 Collections.emptyMap(); 077 078 /** 079 * Constructs a PropertyRequest from an array of PropertyRequestItem objects. 080 * If there are two entries for the same PropertyName, the PropertyRequest values 081 * for those entries are logically merged. 082 * 083 * @param items The array of PropertyRequestItem objects that are to 084 * be combined into a new PropertyRequest. 085 * A <b>null</b> or empty array produces an empty PropertyRequest. 086 */ 087 public PropertyRequest(PropertyRequestItem... items) 088 { 089 this(getItemMap(items)); 090 } 091 092 /** 093 * Constructs a PropertyRequest from an array of PropertyRequestItem objects. 094 * If there are two entries for the same PropertyName, the PropertyRequest values 095 * for those entries are logically merged. 096 * 097 * @param items The array of PropertyRequestItem objects that are to 098 * be combined into a new PropertyRequest. 099 * A <b>null</b> or empty array produces an empty PropertyRequest. 100 */ 101 public PropertyRequest(PropertyName<?>[] items) 102 { 103 this((PropertyRequestItem[]) items); 104 } 105 106 /** 107 * Constructs a PropertyRequest from an array of PropertyRequestItem objects. 108 * If there are two entries for the same PropertyName, the PropertyRequest values 109 * for those entries are logically merged. 110 * 111 * @param items The array of PropertyRequestItem objects that are to 112 * be combined into a new PropertyRequest. 113 * A <b>null</b> or empty array produces an empty PropertyRequest. 114 */ 115 public PropertyRequest(NestedPropertyName<?>[] items) 116 { 117 this((PropertyRequestItem[]) items); 118 } 119 120 /** 121 * Constructs a PropertyRequest from a <PropertyName, PropertyRequest> map. 122 */ 123 private PropertyRequest(Map<PropertyName<?>, PropertyRequest> itemMap) 124 { 125 _itemMap = itemMap == null || itemMap.isEmpty()? null 126 :Collections.unmodifiableMap(itemMap); 127 } 128 129 private static Map<PropertyName<?>, PropertyRequest> 130 getItemMap(PropertyRequestItem... items) 131 { 132 if (items == null || items.length == 0) { 133 return null; 134 } else { 135 Map<PropertyName<?>, PropertyRequest> itemMap 136 = new HashMap<PropertyName<?>, PropertyRequest>(items.length); 137 138 for (PropertyRequestItem request: items) { 139 if (request == null) { 140 // ignore 141 } else if (request instanceof PropertyName) { 142 put(itemMap, (PropertyName<?>)request, null); 143 } else if (request instanceof NestedPropertyName) { 144 NestedPropertyName<?> npn = (NestedPropertyName<?>)request; 145 put(itemMap, npn.getRoot(), npn.getNested()); 146 } else if (request instanceof PropertyNameList) { 147 PropertyNameList list = (PropertyNameList) request; 148 149 for (PropertyName<?> name : list.getPropertyNames()) 150 put(itemMap, name, null); 151 } else if (request instanceof PropertyRequest) { 152 Map<PropertyName<?>, PropertyRequest> map = 153 ((PropertyRequest) request).toMap(); 154 155 for (Entry<PropertyName<?>, PropertyRequest> entry : map.entrySet()) 156 put(itemMap, entry.getKey(), entry.getValue()); 157 } else { 158 throw new UnsupportedOperationException(request.getClass().getName() 159 + " cannot be a PropertyRequest item"); //$NON-NLS-1$ 160 } 161 } 162 163 return itemMap.isEmpty()? null: itemMap; 164 } 165 } 166 167 private static void put(Map<PropertyName<?>, PropertyRequest> itemMap, 168 PropertyName<?> propertyName, 169 PropertyRequest propertyRequest) { 170 PropertyRequest pr = itemMap.get(propertyName); 171 itemMap.put(propertyName, merge(pr, propertyRequest)); 172 } 173 174 private static PropertyRequest merge(PropertyRequest pr1, PropertyRequest pr2) { 175 if (pr1 == null) { 176 return pr2; 177 } 178 if (pr2 == null) { 179 return pr1; 180 } 181 Map<PropertyName<?>, PropertyRequest> itemMap 182 = new HashMap<PropertyName<?>, PropertyRequest>(pr1.size() + pr2.size()); 183 Iterator<PropertyName<?>> keys = pr1.toMap().keySet().iterator(); 184 while (keys.hasNext()) { 185 PropertyName<?> pName = keys.next(); 186 put(itemMap, pName, pr1.toMap().get(pName)); 187 } 188 keys = pr2.toMap().keySet().iterator(); 189 while (keys.hasNext()) { 190 PropertyName<?> pName = keys.next(); 191 put(itemMap, pName, pr2.toMap().get(pName)); 192 } 193 PropertyRequest result = new PropertyRequest(); 194 result._itemMap = Collections.unmodifiableMap(itemMap); 195 return result; 196 } 197 198 public Map<PropertyName<?>, PropertyRequest> toMap() 199 { 200 return (_itemMap == null) ? EMPTY_MAP : _itemMap; 201 } 202 203 /** 204 * Returns an array containing all of the elements in this PropertyRequest. 205 */ 206 public NestedPropertyName<?>[] toArray() 207 { 208 if (_itemMap == null) { 209 return EMPTY_PNA; 210 } 211 if (_itemArray == null) { 212 NestedPropertyName<?>[] itemArray = new NestedPropertyName<?>[_itemMap.size()]; 213 int i = 0; 214 for (PropertyName<?> propName: _itemMap.keySet()) { 215 PropertyRequest propRequest = _itemMap.get(propName); 216 itemArray[i++] = propName.nest(propRequest); 217 } 218 219 _itemArray = itemArray; 220 } 221 return _itemArray; 222 } 223 224 /** 225 * @see java.util.Map#isEmpty 226 */ 227 public boolean isEmpty() 228 { 229 return toMap().isEmpty(); 230 } 231 232 /** 233 * @see java.util.Map#size 234 */ 235 public int size() 236 { 237 return toMap().size(); 238 } 239 240 /** 241 * @see java.util.Map#get 242 */ 243 public PropertyRequest get(PropertyName<?> key) 244 { 245 return toMap().get(key); 246 } 247 248 @Override 249 public boolean equals(Object object) 250 { 251 if (object == null || getClass() != object.getClass()) { 252 return false; 253 } 254 return toMap().equals(((PropertyRequest)object).toMap()); 255 } 256 257 @Override 258 public int hashCode() 259 { 260 return toMap().hashCode(); 261 } 262 263 @Override 264 public String toString() 265 { 266 StringBuffer builder = new StringBuffer(); 267 NestedPropertyName<?>[] items = toArray(); 268 builder.append('['); 269 for (int i=0; i<items.length; i++) { 270 if (builder.length() > 1) 271 builder.append(", "); //$NON-NLS-1$ 272 builder.append(items[i].toString()); 273 } 274 builder.append(']'); 275 276 return builder.toString(); 277 } 278 279 public PropertyRequest getPropertyRequestForModified() { 280 return null; 281 } 282 283 public PropertyRequest getPropertyRequestForResult() { 284 return this; 285 } 286 287 public boolean isAbortRequested() { 288 return false; 289 } 290 291 public Feedback nest() { 292 return EMPTY; 293 } 294 295 public Feedback nest(PropertyRequest propertyRequest) { 296 return propertyRequest; 297 } 298 299 public Feedback nest(int percentCompleted) { 300 return EMPTY; 301 } 302 303 public Feedback nest(PropertyRequest resultPropertyRequest, int percentCompleted) { 304 return resultPropertyRequest; 305 } 306 307 public String format(String fmt, Object...arguments) { 308 return MessageFormat.format(fmt, arguments); 309 } 310 311 public void notifyActive(String message) { 312 } 313 314 public void notifyWarning(String message) { 315 } 316 317 public void notifyIsModified(Resource resource) { 318 } 319 320 public void notifyPercentComplete(int percentComplete) { 321 } 322 323 } 324 325 /** 326 * A NestedPropertyName consists of the name of a root property whose value is 327 * desired and an optional request for nested properties of the resource (or 328 * resources) referenced by the value of that root property. 329 */ 330 public static class NestedPropertyName<T> implements PropertyRequestItem 331 { 332 final private PropertyNameList.PropertyName<T> _root; 333 final private PropertyRequest _nested; 334 private volatile int _hashCode = 0; 335 336 /** 337 * Constructs a NestedPropertyName from its constituent parts. 338 * 339 * @param root The root PropertyName of the request; must not be <b>null</b>. 340 * @param items The PropertyRequest for the value(s) of the root 341 * property; may be <b>null</b> or empty to request no 342 * nested properties. 343 */ 344 public NestedPropertyName(PropertyName<T> root, 345 PropertyRequestItem... items) { 346 347 if (root == null) { 348 throw new UnsupportedOperationException 349 ("The root PropertyName of a NestedPropertyName cannot be null."); //$NON-NLS-1$ 350 } 351 352 _root = root; 353 354 Map<PropertyName<?>, PropertyRequest> itemMap = 355 PropertyRequest.getItemMap(items); 356 357 _nested = 358 itemMap == null || itemMap.isEmpty() 359 ? null 360 : new PropertyRequest(itemMap); 361 } 362 363 /** 364 * Constructs a NestedPropertyName from its constituent parts. 365 * 366 * @param root The root PropertyName of the request; must not be <b>null</b>. 367 * @param items The PropertyRequest for the value(s) of the root 368 * property; may be <b>null</b> or empty to request no 369 * nested properties. 370 */ 371 public NestedPropertyName(PropertyName<T> root, 372 PropertyName<?>[] items) 373 { 374 this(root, (PropertyRequestItem[])items); 375 } 376 377 /** 378 * Constructs a NestedPropertyName from its constituent parts. 379 * 380 * @param root The root PropertyName of the request; must not be <b>null</b>. 381 * @param items The PropertyRequest for the value(s) of the root 382 * property; may be <b>null</b> or empty to request no 383 * nested properties. 384 */ 385 public NestedPropertyName(PropertyName<T> root, 386 NestedPropertyName<?>[] items) 387 { 388 this(root, (PropertyRequestItem[])items); 389 } 390 391 /** 392 * @return The root PropertyName of this nested property name object; 393 * will never be <b>null</b>. 394 */ 395 public PropertyName<T> getRoot() { 396 return _root; 397 } 398 399 /** 400 * @return The nested PropertyRequest, specifying the properties to be 401 * requested from the resource or resources that are the value of the 402 * root property name of this nested property name. 403 */ 404 public PropertyRequest getNested() { 405 return _nested==null ? PropertyRequest.EMPTY: _nested; 406 } 407 408 /** 409 * Indicates whether some other object is "equal to" this one. 410 * 411 * @param o the object to compare with. 412 * @return true if and only if the specified object is a 413 * NestedPropertyName whose root property name and nested 414 * property name list values are equal to those of this object. 415 */ 416 @Override 417 public boolean equals(Object o) 418 { 419 if (o == null || this.getClass() != o.getClass()) { 420 return false; 421 } 422 NestedPropertyName<?> npn = (NestedPropertyName<?>) o; 423 return _root.equals(npn.getRoot()) 424 && getNested().equals(npn.getNested()); 425 } 426 427 /** 428 * Calculate a hash code value for the object. 429 * 430 * @return the hash code for the object. 431 */ 432 @Override 433 public int hashCode() { 434 if (_hashCode == 0) { 435 int result = 17; 436 result = 37 * result + _root.hashCode(); 437 result = 37 * result + getNested().hashCode(); 438 _hashCode = result; 439 } 440 return _hashCode; 441 } 442 443 /** 444 * Returns a string representation of this NestedPropertyName suitable for 445 * diagnostics. 446 * 447 * @return The string representation of this NestedPropertyName formatted as 448 * "property_name [first_nested_property_request, ... , 449 * last_nested_property_request]" 450 */ 451 @Override 452 public String toString() { 453 if (getNested().isEmpty()) 454 return _root.toString(); 455 else 456 return _root.toString() + getNested().toString(); 457 } 458 } 459}