View Javadoc
1 /* 2 * Fields.java 3 * Created on September 15, 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.Field; 29 import java.lang.reflect.Modifier; 30 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 37 /*** 38 * Specification interface for identifying Fields. Instances of this class may be 39 * logically combined using the AND, OR, and NOT operations. Many common use-cases 40 * are supplied via static accessor methods. 41 * 42 * @author Lonnie Pryor 43 * @version $Revision: 1.1 $ 44 */ 45 public abstract class Fields { 46 /*** A specification matching public fields. */ 47 private static final Fields declaredPublic = new Fields() { 48 public boolean evaluate (Field field) { 49 return Modifier.isPublic(field.getModifiers()); 50 } 51 }; 52 /*** A specification matching protected fields. */ 53 private static final Fields declaredProtected = new Fields() { 54 public boolean evaluate (Field field) { 55 return Modifier.isProtected(field.getModifiers()); 56 } 57 }; 58 /*** A specification matching private fields. */ 59 private static final Fields declaredPrivate = new Fields() { 60 public boolean evaluate (Field field) { 61 return Modifier.isPrivate(field.getModifiers()); 62 } 63 }; 64 /*** A specification matching package protected fields/ *//package-summary/html">color="#AA0000">* A specification matching package protected fields/ *//package-summary.html">nt color="#AA0000">/*** A specification matching package protected fields/ *//package-summary.html">color="#AA0000">* A specification matching package protected fields. */ 65 private static final Fields declaredPackageProtected = declaredPublic.or( 66 declaredProtected).or(declaredPrivate).not(); 67 /*** A specification matching final fields. */ 68 private static final Fields declaredFinal = new Fields() { 69 public boolean evaluate (Field field) { 70 return Modifier.isFinal(field.getModifiers()); 71 } 72 }; 73 /*** A specification matching static fields. */ 74 private static final Fields declaredStatic = new Fields() { 75 public boolean evaluate (Field field) { 76 return Modifier.isStatic(field.getModifiers()); 77 } 78 }; 79 /*** A specification matching transient fields. */ 80 private static final Fields declaredTransient = new Fields() { 81 public boolean evaluate (Field field) { 82 return Modifier.isTransient(field.getModifiers()); 83 } 84 }; 85 /*** A specification matching volatile fields. */ 86 private static final Fields declaredVolatile = new Fields() { 87 public boolean evaluate (Field field) { 88 return Modifier.isVolatile(field.getModifiers()); 89 } 90 }; 91 /*** Index of the modifier-based specifications by name. */ 92 private static final Map modifiersByName; 93 94 /*** 95 * Initalize the modifier name mapping. 96 */ 97 static { 98 Map tmp = new HashMap(8); 99 tmp.put("public", declaredPublic); 100 tmp.put("protected", declaredProtected); 101 tmp.put("package", declaredPackageProtected); 102 tmp.put("private", declaredPrivate); 103 tmp.put("static", declaredStatic); 104 tmp.put("final", declaredFinal); 105 tmp.put("transient", declaredTransient); 106 tmp.put("volatile", declaredVolatile); 107 modifiersByName = Collections.unmodifiableMap(tmp); 108 } 109 110 /*** 111 * Creates a new Fields object. 112 */ 113 protected Fields () { 114 } 115 116 /*** 117 * Returns a specification matching public fields. 118 * 119 * @return A specification matching public fields. 120 */ 121 public static Fields declaredPublic () { 122 return declaredPublic; 123 } 124 125 /*** 126 * Returns a specification matching protected fields. 127 * 128 * @return A specification matching protected fields. 129 */ 130 public static Fields declaredProtected () { 131 return declaredProtected; 132 } 133 134 /*** 135 * Returns a specification matching private fields. 136 * 137 * @return A specification matching private fields. 138 */ 139 public static Fields declaredPrivate () { 140 return declaredPrivate; 141 } 142 143 /*** 144 * Returns a specification matching package protected fields. 145 * 146 * @return A specification matching package protected fields. 147 */ 148 public static Fields declaredPackageProtected () { 149 return declaredPackageProtected; 150 } 151 152 /*** 153 * Returns a specification matching final fields. 154 * 155 * @return A specification matching final fields. 156 */ 157 public static Fields declaredFinal () { 158 return declaredFinal; 159 } 160 161 /*** 162 * Returns a specification matching static fields. 163 * 164 * @return A specification matching static fields. 165 */ 166 public static Fields declaredStatic () { 167 return declaredStatic; 168 } 169 170 /*** 171 * Returns a specification matching transient fields. 172 * 173 * @return A specification matching transient fields. 174 */ 175 public static Fields declaredTransient () { 176 return declaredTransient; 177 } 178 179 /*** 180 * Returns a specification matching volatile fields. 181 * 182 * @return A specification matching volatile fields. 183 */ 184 public static Fields declaredVolatile () { 185 return declaredVolatile; 186 } 187 188 /*** 189 * Creates a new specification satisfied by Fields equal to the supplied Field. 190 * 191 * @param toTest The Field to be equal to. 192 * 193 * @return A new specification satisfied by Fields equal to the supplied Field. 194 */ 195 public static Fields equalTo (final Field toTest) { 196 if (toTest == null) 197 throw new NullPointerException("toTest"); 198 return new Fields() { 199 public boolean evaluate (Field field) { 200 return toTest.equals(field); 201 } 202 }; 203 } 204 205 /*** 206 * Creates a new specification satisfied only by Fields declared on the Types 207 * that satisfy the supplied specification. 208 * 209 * @param specification The specification that Types must match. 210 * 211 * @return A new specification satisfied only by Fields declared on the Types 212 * that satisfy the supplied specification. 213 */ 214 public static Fields declaredOn (final Types specification) { 215 if (specification == null) 216 throw new NullPointerException("specification"); 217 return new Fields() { 218 public boolean evaluate (Field field) { 219 return specification.isSatisfiedBy(field.getDeclaringClass()); 220 } 221 }; 222 } 223 224 /*** 225 * Creates a new specification satisfied by Fields whose names match the supplied 226 * pattern. 227 * 228 * @param namePattern The pattern names must match. 229 * 230 * @return A new specification satisfied by Fields whose names match the supplied 231 * pattern. 232 * 233 * @throws ParseException if the name pattern is invalid. 234 */ 235 public static Fields named (String namePattern) { 236 if (namePattern == null) 237 throw new NullPointerException("namePattern"); 238 return named(Strings.parse(namePattern)); 239 } 240 241 /*** 242 * Creates a new specification satisfied by Fields whose names match the supplied 243 * specification. 244 * 245 * @param specification The specification names must match. 246 * 247 * @return A new specification satisfied by Fields whose names match the supplied 248 * specification. 249 */ 250 public static Fields named (final Strings specification) { 251 return new Fields() { 252 public boolean evaluate (Field field) { 253 return specification.isSatisfiedBy(field.getName()); 254 } 255 }; 256 } 257 258 /*** 259 * Creates a new specification satisfied by Fields whose type satisfy the 260 * supplied specification. 261 * 262 * @param specification The specification types must match. 263 * 264 * @return A new specification satisfied by Fields whose type satisfy the 265 * supplied specification. 266 */ 267 public static Fields ofType (final Types specification) { 268 return new Fields() { 269 public boolean evaluate (Field field) { 270 return specification.isSatisfiedBy(field.getType()); 271 } 272 }; 273 } 274 275 /*** 276 * Parses a pattern string into a complete Fields specification. Format: 277 * <pre> 278 * fields ::= name 279 * name ::= [ field-type ] [ types | "(" types-expr ")" "." ] 280 * ( strings | "(" strings-expr ")" ) 281 * field-type ::= [ modifiers ] ( types | "(" types-expr ")" ) 282 * modifiers ::= ( [ "!" ] 283 * ( "public" | "protected" | "package" | "private" 284 * | "static" | "abstract" | "final" ) )... 285 * </pre> 286 * Example: 287 * <pre> 288 * (int | float) com.lonniepryor..*.foo 289 * </pre> 290 * The above matches any int or float field named 'foo', declared on any class in 291 * the 'com.lonniepryor' namespace. 292 * 293 * @param fieldsPattern The pattern definition string. 294 * 295 * @return A specification matching the pattern. 296 * 297 * @throws ParseException if the pattern is invalid. 298 */ 299 public static Fields parse (String fieldsPattern) { 300 if (fieldsPattern == null) 301 throw new NullPointerException("fieldPattern"); 302 int startIndex = Expression.rtrim(fieldsPattern, 0, fieldsPattern.length()); 303 if (startIndex == 0) 304 throw new ParseException("Illegal empty fields pattern"); 305 return readName(fieldsPattern, startIndex); 306 } 307 308 /*** 309 * Reads a name expression from the pattern. 310 * 311 * @param fieldPattern The pattern definition string. 312 * @param index The effective end of the pattern. 313 * 314 * @return The specification for the field name. 315 * 316 * @throws ParseException if the pattern is invalid. 317 */ 318 private static Fields readName (String fieldPattern, int index) { 319 int nameStart = Expression.statementBeginsAt(fieldPattern, 0, index, "."); 320 if (nameStart < 0) 321 throw new ParseException("Unmatched parenthesis in '" + fieldPattern + "'"); 322 Fields specification = named( 323 Strings.parseExpression(fieldPattern.substring(nameStart, index))); 324 int nextIndex = Expression.rtrim(fieldPattern, 0, nameStart); 325 if ((nextIndex > 0) && (fieldPattern.charAt(nextIndex - 1) == '.')) { 326 int typeEnd = Expression.rtrim(fieldPattern, 0, nextIndex - 1); 327 if (typeEnd == 0) 328 throw new ParseException( 329 "Declaring type not found in '" + fieldPattern + "'"); 330 int typeBegin = Expression.statementBeginsAt(fieldPattern, 0, typeEnd); 331 if (typeBegin < 0) 332 throw new ParseException("Unmatched parenthesis in '" + fieldPattern + "'"); 333 specification = specification.and( 334 declaredOn( 335 Types.parseExpression(fieldPattern.substring(typeBegin, typeEnd)))); 336 nextIndex = Expression.rtrim(fieldPattern, 0, typeBegin); 337 } 338 if (nextIndex > 0) 339 specification = specification.and(readFieldType(fieldPattern, nextIndex)); 340 return specification; 341 } 342 343 /*** 344 * Reads a field type expression from the pattern. 345 * 346 * @param fieldPattern The pattern definition string. 347 * @param index The effective end of the pattern. 348 * 349 * @return The specification for the field type. 350 * 351 * @throws ParseException if the pattern is invalid. 352 */ 353 private static Fields readFieldType (String fieldPattern, int index) { 354 int typeStart = Expression.statementBeginsAt(fieldPattern, 0, index); 355 if (typeStart < 0) 356 throw new ParseException("Unmatched parenthesis in '" + fieldPattern + "'"); 357 Fields specification = ofType( 358 Types.parseExpression(fieldPattern.substring(typeStart, index))); 359 int modsEnd = Expression.rtrim(fieldPattern, 0, typeStart); 360 if (modsEnd == 0) 361 return specification; 362 return specification.and(readModifiers(fieldPattern, modsEnd)); 363 } 364 365 /*** 366 * Reads a field modifier expression from the pattern. 367 * 368 * @param fieldPattern The pattern definition string. 369 * @param index The effective end of the pattern. 370 * 371 * @return The specification for the field type. 372 * 373 * @throws ParseException if the pattern is invalid. 374 */ 375 private static Fields readModifiers (String fieldPattern, int index) { 376 int nextIndex = index; 377 while ( 378 (nextIndex > 0) 379 && !Character.isWhitespace(fieldPattern.charAt(nextIndex - 1)) 380 && (fieldPattern.charAt(nextIndex - 1) != '!')) 381 --nextIndex; 382 Fields specification = (Fields)modifiersByName.get( 383 fieldPattern.substring(nextIndex, index)); 384 if (specification == null) 385 throw new ParseException( 386 "Unknown modifier '" + fieldPattern.substring(nextIndex, index) + "' in '" 387 + fieldPattern + "'"); 388 nextIndex = Expression.rtrim(fieldPattern, 0, nextIndex); 389 if ((nextIndex > 0) && (fieldPattern.charAt(nextIndex - 1) == '!')) { 390 specification = specification.not(); 391 nextIndex = Expression.rtrim(fieldPattern, 0, nextIndex - 1); 392 } 393 if (nextIndex == 0) 394 return specification; 395 return specification.and(readModifiers(fieldPattern, nextIndex)); 396 } 397 398 /*** 399 * Returns true if the supplied Field is not null and satisfies this 400 * specification. 401 * 402 * @param field The Field to test. 403 * 404 * @return True if the supplied Field is not null and satisfies this 405 * specification. 406 */ 407 public final boolean isSatisfiedBy (Field field) { 408 return (field != null) && evaluate(field); 409 } 410 411 /*** 412 * Returns true if the supplied Field satisfies this specification. 413 * 414 * @param attr The Field to test. 415 * 416 * @return True if the supplied Field satisfies this specification. 417 */ 418 public abstract boolean evaluate (Field field); 419 420 /*** 421 * Returns true if all of the supplied Fields satisfy this specification. 422 * 423 * @param all The array of Fields to test. 424 * 425 * @return True if all of the supplied Fields satisfy this specification. 426 */ 427 public final boolean isSatisifiedByAll (Field[] all) { 428 if (all == null) 429 throw new NullPointerException("all"); 430 for (int i = 0; i < all.length; ++i) 431 if (!isSatisfiedBy(all[i])) 432 return false; 433 return true; 434 } 435 436 /*** 437 * Returns true if any of the supplied Fields satisfy this specification. 438 * 439 * @param any The array of Fields to test. 440 * 441 * @return True if any of the supplied Fields satisfy this specification. 442 */ 443 public final boolean isSatisifiedByAny (Field[] any) { 444 if (any == null) 445 throw new NullPointerException("any"); 446 for (int i = 0; i < any.length; ++i) 447 if (isSatisfiedBy(any[i])) 448 return true; 449 return false; 450 } 451 452 /*** 453 * Selects the first Field that satisfies this specification from the supplied 454 * array. 455 * 456 * @param from The array to select from. 457 * 458 * @return The first Field that satisfies this specification from the supplied 459 * array. 460 */ 461 public final Field selectFirst (Field[] from) { 462 if (from == null) 463 throw new NullPointerException("from"); 464 for (int i = 0; i < from.length; ++i) 465 if (isSatisfiedBy(from[i])) 466 return from[i]; 467 return null; 468 } 469 470 /*** 471 * Selects all the Fields that satisfy this specification from the supplied 472 * array. 473 * 474 * @param from The array to select from. 475 * 476 * @return All the Fields that satisfy this specification from the supplied 477 * array. 478 */ 479 public final Field[] selectAll (Field[] from) { 480 if (from == null) 481 throw new NullPointerException("from"); 482 List results = new ArrayList(from.length); 483 for (int i = 0; i < from.length; ++i) 484 if (isSatisfiedBy(from[i])) 485 results.add(from[i]); 486 return (Field[])results.toArray(new Field[results.size()]); 487 } 488 489 /*** 490 * Returns a specification representing a logical AND of this specification on 491 * the left and the supplied specification on the right. 492 * 493 * @param specification The specification to AND with. 494 * 495 * @return A specification representing a logical AND of this specification on 496 * the left and the supplied specification on the right. 497 */ 498 public final Fields and (final Fields specification) { 499 if (specification == null) 500 throw new NullPointerException("specification"); 501 return new Fields() { 502 public boolean evaluate (Field field) { 503 return Fields.this.evaluate(field) && specification.evaluate(field); 504 } 505 }; 506 } 507 508 /*** 509 * Returns a specification representing a logical OR of this specification on the 510 * left and the supplied specification on the right. 511 * 512 * @param specification The specification to OR with. 513 * 514 * @return A specification representing a logical OR of this specification on the 515 * left and the supplied specification on the right. 516 */ 517 public final Fields or (final Fields specification) { 518 if (specification == null) 519 throw new NullPointerException("specification"); 520 return new Fields() { 521 public boolean evaluate (Field field) { 522 return Fields.this.evaluate(field) || specification.evaluate(field); 523 } 524 }; 525 } 526 527 /*** 528 * Returns a specification representing a logical NOT of this specification. 529 * 530 * @return A specification representing a logical NOT of this specification. 531 */ 532 public final Fields not () { 533 return new Fields() { 534 public boolean evaluate (Field field) { 535 return !Fields.this.evaluate(field); 536 } 537 }; 538 } 539 }

This page was automatically generated by Maven