1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package net.jini.core.constraint; 20 21 import java.io.IOException; 22 import java.io.InvalidObjectException; 23 import java.io.ObjectInputStream; 24 import java.io.ObjectStreamField; 25 import java.io.Serializable; 26 import java.util.Collection; 27 import java.util.Set; 28 import org.apache.river.api.io.AtomicSerial; 29 import org.apache.river.api.io.AtomicSerial.GetArg; 30 31 /** 32 * Combines two or more constraint alternatives into a single overall 33 * constraint. The semantics of this aggregate constraint are that at least 34 * one of the individual constraint alternatives must be satisfied. The 35 * alternatives do not have to be instances of the same type, but they 36 * cannot themselves be <code>ConstraintAlternatives</code> instances. 37 * <p> 38 * Note that this class implements {@link RelativeTimeConstraint} even though 39 * the constraint elements might not implement 40 * <code>RelativeTimeConstraint</code>. 41 * <p> 42 * An instance containing an exhaustive list of alternatives (for example, 43 * an instance containing both <code>ClientAuthentication.YES</code> and 44 * <code>ClientAuthentication.NO</code>) serves no useful purpose, as a 45 * requirement or as a preference. A <i>don't care</i> condition should 46 * be expressed by the <i>absence</i> of constraints. 47 * 48 * @author Sun Microsystems, Inc. 49 * @since 2.0 50 */ 51 @AtomicSerial 52 public final class ConstraintAlternatives 53 implements RelativeTimeConstraint, Serializable 54 { 55 private static final long serialVersionUID = 7214615235302870613L; 56 57 /** 58 * @serialField constraints InvocationConstraint[] 59 * The alternative constraints. 60 */ 61 private static final ObjectStreamField[] serialPersistentFields = { 62 new ObjectStreamField("constraints", InvocationConstraint[].class, 63 true) 64 }; 65 66 /** 67 * The alternative constraints. 68 */ 69 private final InvocationConstraint[] constraints; 70 71 /** 72 * Indicates whether any of the constraints are based on relative time. 73 */ 74 private transient boolean rel = false; 75 76 /** 77 * Creates an instance containing the specified alternative constraints, 78 * with duplicate constraints removed. The argument passed to this 79 * constructor is neither modified nor retained; subsequent changes to 80 * that argument have no effect on the instance created. 81 * 82 * @param constraints the alternative constraints 83 * @throws NullPointerException if the argument is <code>null</code> or 84 * any element is <code>null</code> 85 * @throws IllegalArgumentException if any of the elements are instances 86 * of <code>ConstraintAlternatives</code>, or if fewer than two elements 87 * remain after duplicate constraints are removed 88 */ 89 public ConstraintAlternatives(InvocationConstraint[] constraints) { 90 this.constraints = 91 reduce((InvocationConstraint[]) constraints.clone()); 92 setRelative(); 93 } 94 95 /** 96 * AtomicSerial constructor. 97 * 98 * @param arg atomic deserialization parameter 99 * @throws java.io.IOException if there are I/O errors while reading from GetArg's 100 * underlying <code>InputStream</code> 101 * @throws InvalidObjectException if object invariants aren't satisfied. 102 */ 103 public ConstraintAlternatives(GetArg arg) throws IOException{ 104 this(validate(arg.get("constraints", null, InvocationConstraint[].class)), false); 105 } 106 107 private static InvocationConstraint[] validate(InvocationConstraint[] constraints) 108 throws InvalidObjectException{ 109 if (constraints == null) { 110 throw new InvalidObjectException( 111 "cannot create constraint with no elements"); 112 } 113 constraints = constraints.clone(); 114 try { 115 verify(constraints, 2); 116 } catch (RuntimeException e) { 117 if (e instanceof NullPointerException || 118 e instanceof IllegalArgumentException) 119 { 120 InvalidObjectException ee = 121 new InvalidObjectException(e.getMessage()); 122 ee.initCause(e); 123 throw ee; 124 } 125 throw e; 126 } 127 for (int i = constraints.length; --i >= 0; ) { 128 if (Constraint.contains(constraints, i, constraints[i])) { 129 throw new InvalidObjectException( 130 "cannot create constraint with duplicate elements"); 131 } 132 } 133 return constraints; 134 } 135 136 /** 137 * Creates an instance containing the specified alternative constraints, 138 * with duplicate constraints removed. The argument passed to this 139 * constructor is neither modified nor retained; subsequent changes to 140 * that argument have no effect on the instance created. 141 * 142 * @param c the alternative constraints 143 * @throws NullPointerException if the argument is <code>null</code> or 144 * any element is <code>null</code> 145 * @throws IllegalArgumentException if any of the elements are instances 146 * of <code>ConstraintAlternatives</code>, or if the elements are not all 147 * instances of <code>InvocationConstraint</code>, or if fewer than two 148 * elements remain after duplicate constraints are removed 149 */ 150 public ConstraintAlternatives(Collection<InvocationConstraint> c) { 151 try { 152 constraints = reduce( c.toArray( 153 new InvocationConstraint[c.size()])); 154 } catch (ArrayStoreException e) { 155 throw new IllegalArgumentException( 156 "element of collection is not an InvocationConstraint"); 157 } 158 setRelative(); 159 } 160 161 /** 162 * Returns a constraint representing the specified alternative constraints, 163 * with duplicate constraints removed. If a single constraint remains after 164 * duplicates are removed, then that constraint is returned, otherwise an 165 * instance of <code>ConstraintAlternatives</code> containing the remaining 166 * constraints is returned. The argument passed to this method is neither 167 * modified nor retained; subsequent changes to that argument have no 168 * effect on the instance created. 169 * 170 * @param constraints the alternative constraints 171 * @return a constraint representing the specified alternative constraints, 172 * with duplicate constraints removed 173 * @throws NullPointerException if the argument is <code>null</code> or 174 * any element is <code>null</code> 175 * @throws IllegalArgumentException if the argument is empty, or if any 176 * of the elements are instances of <code>ConstraintAlternatives</code> 177 */ 178 public static InvocationConstraint create( 179 InvocationConstraint[] constraints) 180 { 181 return reduce((InvocationConstraint[]) constraints.clone(), false); 182 } 183 184 /** 185 * Returns a constraint representing the specified alternative constraints, 186 * with duplicate constraints removed. If a single constraint remains after 187 * duplicates are removed, then that constraint is returned, otherwise an 188 * instance of <code>ConstraintAlternatives</code> containing the remaining 189 * constraints is returned. The argument passed to this method is neither 190 * modified nor retained; subsequent changes to that argument have no 191 * effect on the instance created. 192 * 193 * @param c the alternative constraints 194 * @return a constraint representing the specified alternative constraints, 195 * with duplicate constraints removed 196 * @throws NullPointerException if the argument is <code>null</code> or 197 * any element is <code>null</code> 198 * @throws IllegalArgumentException if the argument is empty, or if any 199 * of the elements are instances of <code>ConstraintAlternatives</code>, 200 * or if the elements are not all instances of 201 * <code>InvocationConstraint</code> 202 */ 203 public static InvocationConstraint create(Collection<InvocationConstraint> c) { 204 try { 205 return reduce( c.toArray( new InvocationConstraint[c.size()]), false); 206 } catch (ArrayStoreException e) { 207 throw new IllegalArgumentException( 208 "element of collection is not an InvocationConstraint"); 209 } 210 } 211 212 /** 213 * Creates a constraint containing the specified alternative constraints, 214 * and computes the rel field if allAbs is false. 215 */ 216 private ConstraintAlternatives(InvocationConstraint[] constraints, 217 boolean allAbs) 218 { 219 this.constraints = constraints; 220 if (!allAbs) { 221 setRelative(); 222 } 223 } 224 225 /** 226 * Sets the rel field to true if any of the constraints are relative. 227 */ 228 private void setRelative() { 229 for (int i = constraints.length; --i >= 0; ) { 230 if (constraints[i] instanceof RelativeTimeConstraint) { 231 rel = true; 232 return; 233 } 234 } 235 } 236 237 /** 238 * Returns true if any of the constraints are relative, false otherwise. 239 */ 240 boolean relative() { 241 return rel; 242 } 243 244 /** 245 * Verifies that the array is non-empty, and that the elements are all 246 * non-null and not ConstraintAlternatives instances. Removes duplicates 247 * and returns a single constraint if there's only one left, otherwise 248 * returns an ConstraintAlternatives containing the remaining constraints. 249 * The argument is modified in place. 250 */ 251 private static InvocationConstraint reduce( 252 InvocationConstraint[] constraints, 253 boolean allAbs) 254 { 255 verify(constraints, 1); 256 int n = reduce0(constraints); 257 if (n == 1) { 258 return constraints[0]; 259 } 260 return new ConstraintAlternatives( 261 (InvocationConstraint[]) Constraint.trim(constraints, n), 262 allAbs); 263 } 264 265 /** 266 * Verifies that the array has at least min elements, and that the 267 * elements are all non-null and not ConstraintAlternatives instances. 268 */ 269 private static void verify(InvocationConstraint[] constraints, int min) { 270 if (constraints.length < min) { 271 throw new IllegalArgumentException( 272 "cannot create constraint with " + 273 (min == 1 ? "no" : ("less than " + min)) + 274 " elements"); 275 } 276 for (int i = constraints.length; --i >= 0; ) { 277 InvocationConstraint c = constraints[i]; 278 if (c == null) { 279 throw new NullPointerException("elements cannot be null"); 280 } else if (c instanceof ConstraintAlternatives) { 281 throw new IllegalArgumentException( 282 "elements cannot be ConstraintAlternatives instances"); 283 } 284 } 285 } 286 287 /** 288 * Verifies that the array has at least 2 elements, and that the elements 289 * are all non-null and not ConstraintAlternatives instances, removes 290 * duplicates, modifying the array in place, verifies that there are still 291 * at least 2 elements, and returns an array containing the remaining 292 * elements. 293 */ 294 private static InvocationConstraint[] reduce( 295 InvocationConstraint[] constraints) 296 { 297 verify(constraints, 2); 298 int n = reduce0(constraints); 299 if (n == 1) { 300 throw new IllegalArgumentException( 301 "reduced to less than 2 elements"); 302 } 303 return (InvocationConstraint[]) Constraint.trim(constraints, n); 304 } 305 306 /** 307 * Eliminates duplicates, modifying the array in place, and returns 308 * the resulting number of elements. 309 */ 310 private static int reduce0(InvocationConstraint[] constraints) { 311 int i = 0; 312 for (int j = 0; j < constraints.length; j++) { 313 InvocationConstraint c = constraints[j]; 314 if (!Constraint.contains(constraints, i, c)) { 315 constraints[i++] = c; 316 } 317 } 318 return i; 319 } 320 321 /** 322 * Returns an immutable set of all of the constraints. Any attempt to 323 * modify this set results in an {@link UnsupportedOperationException} 324 * being thrown. 325 * 326 * @return an immutable set of all of the constraints 327 */ 328 public Set<InvocationConstraint> elements() { 329 return new ArraySet<InvocationConstraint>(constraints); 330 } 331 332 /** 333 * Returns the elements, without copying. 334 */ 335 InvocationConstraint[] getConstraints() { 336 return constraints; 337 } 338 339 /** 340 * Returns a constraint equal to the result of taking the constraints in 341 * this instance, replacing each constraint that is an instance of 342 * {@link RelativeTimeConstraint} with the result of invoking that 343 * constraint's <code>makeAbsolute</code> method with the specified base 344 * time, and invoking the <code>create</code> method of this class with 345 * the revised collection of constraints. 346 */ 347 public InvocationConstraint makeAbsolute(long baseTime) { 348 if (!rel) { 349 return this; 350 } 351 InvocationConstraint[] vals = 352 new InvocationConstraint[constraints.length]; 353 for (int i = vals.length; --i >= 0; ) { 354 InvocationConstraint c = constraints[i]; 355 if (c instanceof RelativeTimeConstraint) { 356 c = ((RelativeTimeConstraint) c).makeAbsolute(baseTime); 357 } 358 vals[i] = c; 359 } 360 return reduce(vals, true); 361 } 362 363 /** 364 * Returns a hash code value for this object. 365 */ 366 public int hashCode() { 367 return Constraint.hash(constraints); 368 } 369 370 /** 371 * Two instances of this class are equal if they have the same constraints 372 * (ignoring order). 373 */ 374 public boolean equals(Object obj) { 375 return (obj instanceof ConstraintAlternatives && 376 Constraint.equal(constraints, 377 ((ConstraintAlternatives) obj).constraints)); 378 } 379 380 /** 381 * Returns a string representation of this object. 382 */ 383 public String toString() { 384 return "ConstraintAlternatives" + Constraint.toString(constraints); 385 } 386 387 /** 388 * Verifies that there are at least two constraints, that none are 389 * <code>null</code> and none are instances of this class, and that 390 * there are no duplicates. 391 * 392 * @throws InvalidObjectException if there are less than two constraints, 393 * or any constraint is <code>null</code> or an instance of this class, 394 * or if there are duplicates 395 * @param s ObjectInputStream 396 * @throws ClassNotFoundException if class not found. 397 * @throws IOException if a problem occurs during de-serialization. 398 */ 399 private void readObject(ObjectInputStream s) 400 throws IOException, ClassNotFoundException 401 { 402 s.defaultReadObject(); 403 if (constraints == null) { 404 throw new InvalidObjectException( 405 "cannot create constraint with no elements"); 406 } 407 try { 408 verify(constraints, 2); 409 } catch (RuntimeException e) { 410 if (e instanceof NullPointerException || 411 e instanceof IllegalArgumentException) 412 { 413 InvalidObjectException ee = 414 new InvalidObjectException(e.getMessage()); 415 ee.initCause(e); 416 throw ee; 417 } 418 throw e; 419 } 420 for (int i = constraints.length; --i >= 0; ) { 421 if (Constraint.contains(constraints, i, constraints[i])) { 422 throw new InvalidObjectException( 423 "cannot create constraint with duplicate elements"); 424 } 425 } 426 setRelative(); 427 } 428 }