View Javadoc
1 /* 2 * Constructors.java 3 * Created on August 1, 2003 4 * 5 * The Blues Framework - A lightweight application framework 6 * Copyright (C) 2003 Lonnie Pryor 7 * http://blues.lonniepryor.com 8 * 9 * This library is free software; you can redistribute it and/or modify it under the 10 * terms of the GNU Lesser General Public License as published by the Free Software 11 * Foundation; either version 2.1 of the License, or (at your option) any later 12 * version. 13 * 14 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 16 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License along 19 * with this library; if not, write to: 20 * 21 * The Free Software Foundation, Inc. 22 * 59 Temple Place, Suite 330 23 * Boston, MA 02111-1307 USA 24 * 25 */ 26 package com.lonniepryor.blues.util; 27 28 import java.lang.reflect.Constructor; 29 import java.lang.reflect.Modifier; 30 31 import java.util.ArrayList; 32 import java.util.Collection; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.Map; 36 37 /*** 38 * Specification interface for identifying Constructors. Instances of this class 39 * may be logically combined using the AND, OR, and NOT operations. Many common 40 * use-cases are supplied via static accessor methods. 41 * 42 * @author Lonnie Pryor 43 * @version 1.0 44 */ 45 public abstract class Constructors { 46 /*** Specification satisfied by Constructors decalred as public. */ 47 private static final Constructors declaredPublic = new Constructors() { 48 public boolean evaluate (Constructor constructor) { 49 return Modifier.isPublic(constructor.getModifiers()); 50 } 51 }; 52 /*** Specification satisfied by Constructors decalred as protected. */ 53 private static final Constructors declaredProtected = new Constructors() { 54 public boolean evaluate (Constructor constructor) { 55 return Modifier.isProtected(constructor.getModifiers()); 56 } 57 }; 58 /*** Specification satisfied by Constructors decalred as private. */ 59 private static final Constructors declaredPrivate = new Constructors() { 60 public boolean evaluate (Constructor constructor) { 61 return Modifier.isPrivate(constructor.getModifiers()); 62 } 63 }; 64 /*** Specification satisfied by Constructors decalred as package protected/ *//package-summary/html">color="#AA0000">* Specification satisfied by Constructors decalred as package protected/ *//package-summary.html">nt color="#AA0000">/*** Specification satisfied by Constructors decalred as package protected/ *//package-summary.html">color="#AA0000">* Specification satisfied by Constructors decalred as package protected. */ 65 private static final Constructors declaredPackageProtected = declaredPublic.or( 66 declaredProtected).or(declaredPrivate).not(); 67 /*** Specification satisfied by Constructors that take no parameters. */ 68 private static final Constructors takingNoParameters = taking( 69 Parameters.withNoElements()); 70 /*** Specification satisfied by Constructors that take exactly one parameter. */ 71 private static final Constructors takingOneParameter = taking( 72 Parameters.withOneElement()); 73 /*** Specification satisfied by public Constructors that take no parameters. */ 74 private static final Constructors publicFactories = takingNoParameters.and( 75 declaredPublic); 76 /*** Index of the modifier-based specifications by name. */ 77 private static final Map modifiersByName; 78 79 /*** 80 * Initalize the modifier name mapping. 81 */ 82 static { 83 Map tmp = new HashMap(4); 84 tmp.put("public", declaredPublic); 85 tmp.put("protected", declaredProtected); 86 tmp.put("package", declaredPackageProtected); 87 tmp.put("private", declaredPrivate); 88 modifiersByName = Collections.unmodifiableMap(tmp); 89 } 90 91 /*** 92 * Creates a new Constructors object. 93 */ 94 protected Constructors () { 95 } 96 97 /*** 98 * Returns a specification satisfied by Constructors decalred as public. 99 * 100 * @return A specification satisfied by Constructors decalred as public. 101 */ 102 public static Constructors declaredPublic () { 103 return declaredPublic; 104 } 105 106 /*** 107 * Returns a specification satisfied by Constructors decalred as protected. 108 * 109 * @return A specification satisfied by Constructors decalred as protected. 110 */ 111 public static Constructors declaredProtected () { 112 return declaredProtected; 113 } 114 115 /*** 116 * Returns a specification satisfied by Constructors decalred as private. 117 * 118 * @return A specification satisfied by Constructors decalred as private. 119 */ 120 public static Constructors declaredPrivate () { 121 return declaredPrivate; 122 } 123 124 /*** 125 * Returns a specification satisfied by Constructors decalred as package 126 * protected. 127 * 128 * @return A specification satisfied by Constructors decalred as package 129 * protected. 130 */ 131 public static Constructors declaredPackageProtected () { 132 return declaredPackageProtected; 133 } 134 135 /*** 136 * Returns a specification satisfied by Constructors that take no parameters. 137 * 138 * @return A specification satisfied by Constructors that take no parameters. 139 */ 140 public static Constructors takingNoParameters () { 141 return takingNoParameters; 142 } 143 144 /*** 145 * Returns a specification satisfied by Constructors that take exactly one 146 * parameter. 147 * 148 * @return A specification satisfied by Constructors that take exactly one 149 * parameter. 150 */ 151 public static Constructors takingOneParameter () { 152 return takingOneParameter; 153 } 154 155 /*** 156 * Returns a specification satisfied by public Constructors that take no 157 * parameters. 158 * 159 * @return A specification satisfied by public Constructors that take no 160 * parameters. 161 */ 162 public static Constructors publicFactories () { 163 return publicFactories; 164 } 165 166 /*** 167 * Creates a new specification satisfied by Constructors equal to the supplied 168 * Constructor. 169 * 170 * @param toTest The Constructor to be equal to. 171 * 172 * @return A new specification satisfied by Constructors equal to the supplied 173 * Constructor. 174 */ 175 public static Constructors equalTo (final Constructor toTest) { 176 if (toTest == null) 177 throw new NullPointerException("toTest"); 178 return new Constructors() { 179 public boolean evaluate (Constructor constructor) { 180 return toTest.equals(constructor); 181 } 182 }; 183 } 184 185 /*** 186 * Creates a new specification satisfied only by Constructors hosted on the Types 187 * that satisfy the supplied specification. 188 * 189 * @param specification The specification that Types must match. 190 * 191 * @return A new specification satisfied only by Constructors hosted on the Types 192 * that satisfy the supplied specification. 193 */ 194 public static Constructors declaredOn (final Types specification) { 195 if (specification == null) 196 throw new NullPointerException("specification"); 197 return new Constructors() { 198 public boolean evaluate (Constructor constructor) { 199 return specification.isSatisfiedBy(constructor.getDeclaringClass()); 200 } 201 }; 202 } 203 204 /*** 205 * Creates a new specification satisfied only by Constructors with parameter 206 * Lists that satisfy the supplied specification. 207 * 208 * @param specification The specification that parameter Lists must match. 209 * 210 * @return A new specification satisfied only by Constructors with parameter 211 * Lists that satisfy the supplied specification. 212 */ 213 public static Constructors taking (final Parameters specification) { 214 if (specification == null) 215 throw new NullPointerException("specification"); 216 return new Constructors() { 217 public boolean evaluate (Constructor constructor) { 218 return specification.isSatisfiedBy(constructor.getParameterTypes()); 219 } 220 }; 221 } 222 223 /*** 224 * Creates a new specification satisfied only by Constructors with throwable sets 225 * that satisfy the supplied specification. 226 * 227 * @param specification The specification that throwable sets must match. 228 * 229 * @return A new specification satisfied only by Constructors with throwable sets 230 * that satisfy the supplied specification. 231 */ 232 public static Constructors throwing (final Throwables specification) { 233 if (specification == null) 234 throw new NullPointerException("specification"); 235 return new Constructors() { 236 public boolean evaluate (Constructor constructor) { 237 return specification.isSatisfiedBy(constructor.getExceptionTypes()); 238 } 239 }; 240 } 241 242 /*** 243 * Parses a pattern string into a complete Constructors specification. Format: 244 * <pre> 245 * constructors ::= throws-clause 246 * throws-clause ::= param-list [ " throws " throwables ] 247 * param-list ::= declaring-type "(" [ parameters ] ")" 248 * declaring-type ::= [ modifiers ] [ types | "(" types-expr ")" "." ] "new" 249 * modifiers ::= ( [ "!" ] 250 * ( "public" | "protected" | "package" | "private" ) )... 251 * </pre> 252 * Example: 253 * <pre> 254 * public (com.lonniepryor..* && !javax..*+).new(..) 255 * </pre> 256 * The above matches public constructors taking any number of parameters on any 257 * class in the 'com.lonniepryor' namespace that is not assignable to a class in 258 * the 'javax' namespace. 259 * 260 * @param ctorsPattern The pattern string. 261 * 262 * @return A specification matching the supplied constructor pattern. 263 * 264 * @see Types#parse(String) 265 * @see Parameters#parse(String) 266 */ 267 public static Constructors parse (String ctorsPattern) { 268 if (ctorsPattern == null) 269 throw new NullPointerException("ctorsPattern"); 270 int startIndex = Expression.rtrim(ctorsPattern, 0, ctorsPattern.length()); 271 if (startIndex == 0) 272 throw new ParseException("Illegal empty constructors pattern"); 273 return readThrowsClause(ctorsPattern, startIndex); 274 } 275 276 /*** 277 * Reads the throws clause. 278 * 279 * @param ctorsPattern The pattern string. 280 * @param index The effective end of the pattern. 281 * 282 * @return A specification matching the supplied constructor pattern. 283 */ 284 private static Constructors readThrowsClause (String ctorsPattern, int index) { 285 int throwsAt = ctorsPattern.lastIndexOf(" throws ", index); 286 int paramsEnds = Expression.rtrim( 287 ctorsPattern, 0, (throwsAt < 0) ? index : throwsAt); 288 if (paramsEnds == 0) 289 throw new ParseException("Parameters not found in '" + ctorsPattern + "'"); 290 Constructors specification = readParamList(ctorsPattern, paramsEnds); 291 if (throwsAt >= 0) 292 specification = specification.and( 293 throwing(Throwables.parse(ctorsPattern.substring(throwsAt + 8, index)))); 294 return specification; 295 } 296 297 /*** 298 * Reads the parameters. 299 * 300 * @param ctorsPattern The pattern string. 301 * @param index The effective end of the pattern. 302 * 303 * @return A specification matching the supplied constructor pattern. 304 */ 305 private static Constructors readParamList (String ctorsPattern, int index) { 306 int paramsBegin = Expression.statementBeginsAt(ctorsPattern, 0, index); 307 if (paramsBegin < 0) 308 throw new ParseException( 309 "Unmatched parameters parenthesis in '" + ctorsPattern + "'"); 310 int nameEnds = Expression.rtrim(ctorsPattern, 0, paramsBegin); 311 if (nameEnds == 0) 312 throw new ParseException("Unable to find 'new' in '" + ctorsPattern + "'"); 313 paramsBegin = Expression.ltrim(ctorsPattern, paramsBegin + 1, index - 1); 314 Constructors specification = taking( 315 (paramsBegin == (index - 1)) ? Parameters.withNoElements() 316 : Parameters.parse( 317 ctorsPattern.substring(paramsBegin, index - 1))); 318 if (!"new".regionMatches(0, ctorsPattern, nameEnds - 3, 3)) 319 throw new ParseException( 320 "Invalid constructor name in '" + ctorsPattern + "'"); 321 int nextIndex = Expression.rtrim(ctorsPattern, 0, nameEnds - 3); 322 if (nextIndex == 0) 323 return specification; 324 return specification.and(readDeclaringType(ctorsPattern, nextIndex)); 325 } 326 327 /*** 328 * Reads the declaring type. 329 * 330 * @param ctorsPattern The pattern string. 331 * @param index The effective end of the pattern. 332 * 333 * @return A specification matching the supplied constructor pattern. 334 */ 335 private static Constructors readDeclaringType (String ctorsPattern, int index) { 336 if (ctorsPattern.charAt(index - 1) == '.') { 337 int typeEnd = Expression.rtrim(ctorsPattern, 0, index - 1); 338 if (typeEnd == 0) 339 throw new ParseException( 340 "Declaring type not found in '" + ctorsPattern + "'"); 341 int typeBegin = Expression.statementBeginsAt(ctorsPattern, 0, typeEnd); 342 if (typeBegin < 0) 343 throw new ParseException("Unmatched parenthesis in '" + ctorsPattern + "'"); 344 Constructors specification = declaredOn( 345 Types.parseExpression(ctorsPattern.substring(typeBegin, typeEnd))); 346 int nextIndex = Expression.rtrim(ctorsPattern, 0, typeBegin); 347 if (nextIndex > 0) 348 specification = specification.and(readModifiers(ctorsPattern, nextIndex)); 349 return specification; 350 } 351 return readModifiers(ctorsPattern, index); 352 } 353 354 /*** 355 * Recursivly reads the modifiers. 356 * 357 * @param ctorsPattern The pattern string. 358 * @param index The effective end of the pattern. 359 * 360 * @return A specification matching the supplied constructor pattern. 361 */ 362 private static Constructors readModifiers (String ctorsPattern, int index) { 363 int nextIndex = index; 364 while ( 365 (nextIndex > 0) 366 && !Character.isWhitespace(ctorsPattern.charAt(nextIndex - 1)) 367 && (ctorsPattern.charAt(nextIndex - 1) != '!')) 368 --nextIndex; 369 Constructors specification = (Constructors)modifiersByName.get( 370 ctorsPattern.substring(nextIndex, index)); 371 if (specification == null) 372 throw new ParseException( 373 "Unknown modifier '" + ctorsPattern.substring(nextIndex, index) + "'"); 374 nextIndex = Expression.rtrim(ctorsPattern, 0, nextIndex); 375 if ((nextIndex > 0) && (ctorsPattern.charAt(nextIndex - 1) == '!')) { 376 specification = specification.not(); 377 nextIndex = Expression.rtrim(ctorsPattern, 0, nextIndex - 1); 378 } 379 if (nextIndex == 0) 380 return specification; 381 return specification.and(readModifiers(ctorsPattern, nextIndex)); 382 } 383 384 /*** 385 * Returns true if the supplied Constructor is not null and satisfies this 386 * specification. 387 * 388 * @param constructor The Constructor to test. 389 * 390 * @return True if the supplied Constructor is not null and satisfies this 391 * specification. 392 */ 393 public final boolean isSatisfiedBy (Constructor constructor) { 394 return (constructor != null) && evaluate(constructor); 395 } 396 397 /*** 398 * Returns true if the supplied Constructor satisfies this specification. 399 * 400 * @param constructor The Constructor to test. 401 * 402 * @return True if the supplied Constructor satisfies this specification. 403 */ 404 protected abstract boolean evaluate (Constructor constructor); 405 406 /*** 407 * Returns true if all of the supplied Constructors satisfy this specification. 408 * 409 * @param all The array of Constructors to test. 410 * 411 * @return True if all of the supplied Constructors satisfy this specification. 412 */ 413 public final boolean isSatisfiedByAll (Constructor[] all) { 414 if (all == null) 415 throw new NullPointerException("all"); 416 for (int i = 0; i < all.length; ++i) 417 if (!isSatisfiedBy(all[i])) 418 return false; 419 return true; 420 } 421 422 /*** 423 * Returns true if any of the supplied Constructors satisfy this specification. 424 * 425 * @param any The array of Constructors to test. 426 * 427 * @return True if any of the supplied Constructors satisfy this specification. 428 */ 429 public final boolean isSatisfiedByAny (Constructor[] any) { 430 if (any == null) 431 throw new NullPointerException("any"); 432 for (int i = 0; i < any.length; ++i) 433 if (isSatisfiedBy(any[i])) 434 return true; 435 return false; 436 } 437 438 /*** 439 * Selects the first Constructor that satisfies this specification from the 440 * supplied array. 441 * 442 * @param from The array to select from. 443 * 444 * @return The first Constructor that satisfies this specification from the 445 * supplied array. 446 */ 447 public final Constructor selectFirst (Constructor[] from) { 448 if (from == null) 449 throw new NullPointerException("from"); 450 for (int i = 0; i < from.length; ++i) 451 if (isSatisfiedBy(from[i])) 452 return from[i]; 453 return null; 454 } 455 456 /*** 457 * Selects all the Constructors that satisfy this specification from the supplied 458 * array. 459 * 460 * @param from The array to select from. 461 * 462 * @return All the Constructors that satisfy this specification from the supplied 463 * array. 464 */ 465 public final Constructor[] selectAll (Constructor[] from) { 466 if (from == null) 467 throw new NullPointerException("from"); 468 Collection results = new ArrayList(); 469 for (int i = 0; i < from.length; ++i) 470 if (isSatisfiedBy(from[i])) 471 results.add(from[i]); 472 return (Constructor[])results.toArray(new Constructor[results.size()]); 473 } 474 475 /*** 476 * Returns a specification representing a logical AND of this specification on 477 * the left and the supplied specification on the right. 478 * 479 * @param specification The specification to AND with. 480 * 481 * @return A specification representing a logical AND of this specification on 482 * the left and the supplied specification on the right. 483 */ 484 public final Constructors and (final Constructors specification) { 485 if (specification == null) 486 throw new NullPointerException("specification"); 487 return new Constructors() { 488 public boolean evaluate (Constructor constructor) { 489 return Constructors.this.evaluate(constructor) 490 && specification.evaluate(constructor); 491 } 492 }; 493 } 494 495 /*** 496 * Returns a specification representing a logical OR of this specification on the 497 * left and the supplied specification on the right. 498 * 499 * @param specification The specification to OR with. 500 * 501 * @return A specification representing a logical OR of this specification on the 502 * left and the supplied specification on the right. 503 */ 504 public final Constructors or (final Constructors specification) { 505 if (specification == null) 506 throw new NullPointerException("specification"); 507 return new Constructors() { 508 public boolean evaluate (Constructor constructor) { 509 return Constructors.this.evaluate(constructor) 510 || specification.evaluate(constructor); 511 } 512 }; 513 } 514 515 /*** 516 * Returns a specification representing a logical NOT of this specification. 517 * 518 * @return A specification representing a logical NOT of this specification. 519 */ 520 public final Constructors not () { 521 return new Constructors() { 522 public boolean evaluate (Constructor constructor) { 523 return !Constructors.this.evaluate(constructor); 524 } 525 }; 526 } 527 }

This page was automatically generated by Maven