View Javadoc
1 /* 2 * Parameters.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.util.ArrayList; 29 import java.util.List; 30 31 /*** 32 * Specification interface for identifying ordered sequences of Types. Instances of 33 * this class may be logically combined using the AND, OR, and NOT operations. 34 * Instances may also be sequentially constructed using the Builder class. Many 35 * common use-cases are supplied via static accessor methods. 36 * 37 * @author Lonnie Pryor 38 * @version $Revision: 1.1 $ 39 */ 40 public abstract class Parameters { 41 /*** A specification satified by non-null Class arrays. */ 42 private static final Parameters any = new Parameters() { 43 protected boolean evaluate (Class[] paramTypes, int index) { 44 return true; 45 } 46 }; 47 /*** A specification satified by empty Class arrays. */ 48 private static final Parameters withNoElements = withNumElements(0); 49 /*** A specification satified by Class arrays with exactly one element. */ 50 private static final Parameters withOneElement = withNumElements(1); 51 52 /*** 53 * Creates a new Parameters object. 54 */ 55 protected Parameters () { 56 } 57 58 /*** 59 * Returns a specification satified by non-null Class arrays. 60 * 61 * @return A specification satified by non-null Class arrays. 62 */ 63 public static Parameters any () { 64 return any; 65 } 66 67 /*** 68 * Returns a specification satified by empty Class arrays. 69 * 70 * @return A specification satified by empty Class arrays. 71 */ 72 public static Parameters withNoElements () { 73 return withNoElements; 74 } 75 76 /*** 77 * Returns a specification satified by Class arrays with exactly one element. 78 * 79 * @return A specification satified by Class arrays with exactly one element. 80 */ 81 public static Parameters withOneElement () { 82 return withOneElement; 83 } 84 85 /*** 86 * Creates a new specification satified by Class arrays with the specified number 87 * of elements. 88 * 89 * @param numberOfElements The number of elements to test for. 90 * 91 * @return A new specification satified by Class arrays with the specified number 92 * of elements. 93 */ 94 public static Parameters withNumElements (final int numberOfElements) { 95 if (numberOfElements < 0) 96 throw new IllegalArgumentException("numberOfElements < 0"); 97 return new Parameters() { 98 protected boolean evaluate (Class[] paramTypes, int index) { 99 return paramTypes.length == numberOfElements; 100 } 101 }; 102 } 103 104 /*** 105 * Creates a new specification satified by Class arrays with one element that 106 * satisifies the supplied specification. 107 * 108 * @param specification The specification to match the single parameter class to. 109 * 110 * @return A new specification satified by Class arrays with one element that 111 * satisifies the supplied specification. 112 */ 113 public static Parameters withElement (Types specification) { 114 if (specification == null) 115 throw new NullPointerException("specification"); 116 return withElements(new Types[] { specification }); 117 } 118 119 /*** 120 * Creates a new specification satified by Class arrays with elements that 121 * satisify the supplied specifications, in order. 122 * 123 * @param specifications The specifications to match the parameter classes to. 124 * 125 * @return A new specification satified by Class arrays with elements that 126 * satisify the supplied specifications, in order. 127 */ 128 public static Parameters withElements (final Types[] specifications) { 129 if (specifications == null) 130 throw new NullPointerException("specifications"); 131 return new Parameters() { 132 protected boolean evaluate (Class[] paramTypes, int index) { 133 if (specifications.length != paramTypes.length) 134 return false; 135 for (int i = 0; i < specifications.length; ++i) 136 if (!specifications[i].isSatisfiedBy(paramTypes[i])) 137 return false; 138 return true; 139 } 140 }; 141 } 142 143 /*** 144 * Returns a Builder whose resulting Parameters specification will allow any 145 * number of inital elements. 146 * 147 * @return A Builder whose resulting Parameters specification will allow any 148 * number of inital elements. 149 */ 150 public static Builder startingWithAny () { 151 return new Builder() { 152 Parameters create (Parameters next) { 153 return newWildcard(next); 154 } 155 }; 156 } 157 158 /*** 159 * Returns a Builder whose resulting Parameters specification will require the 160 * supplied inital element. 161 * 162 * @param specification The specification the inital element must match. 163 * 164 * @return A Builder whose resulting Parameters specification will require the 165 * supplied inital element. 166 */ 167 public static Builder startingWithElement (Types specification) { 168 if (specification == null) 169 throw new NullPointerException("specification"); 170 return startingWithElements(new Types[] { specification }); 171 } 172 173 /*** 174 * Returns a Builder whose resulting Parameters specification will require the 175 * supplied inital elements. 176 * 177 * @param specifications The specifications the inital elements must match. 178 * 179 * @return A Builder whose resulting Parameters specification will require the 180 * supplied inital elements. 181 */ 182 public static Builder startingWithElements (final Types[] specifications) { 183 if (specifications == null) 184 throw new NullPointerException("specifications"); 185 return new Builder() { 186 Parameters create (Parameters next) { 187 return newElements(specifications, next); 188 } 189 }; 190 } 191 192 /*** 193 * Parses a pattern string into a complete Parameters specification. Format: 194 * <pre> 195 * parameters ::= element [ "," element ]... 196 * element ::= ".." | types-expr 197 * </pre> 198 * '..' matches zero or more parameter types, everything else will be treated as a 199 * Types expression. 200 * 201 * @param parametersPattern The content of the pattern. 202 * 203 * @return A Parameters specification from the supplied pattern. 204 * 205 * @throws ParseException if the pattern is invalid. 206 */ 207 public static Parameters parse (String parametersPattern) { 208 if (parametersPattern == null) 209 throw new NullPointerException("parametersPattern"); 210 int start = Expression.ltrim(parametersPattern, 0, parametersPattern.length()); 211 int end = Expression.rtrim( 212 parametersPattern, start, parametersPattern.length()); 213 if (end == start) 214 throw new ParseException("Invalid empty parameters pattern"); 215 int firstComma = parametersPattern.indexOf(','); 216 if (firstComma < 0) { 217 if ("..".regionMatches(0, parametersPattern, start, end - start)) 218 return any; 219 return withElement(Types.parseExpression(parametersPattern)); 220 } 221 int firstElementEnd = Expression.rtrim(parametersPattern, start, firstComma); 222 if (firstElementEnd == start) 223 throw new ParseException("Invalid empty first parameters pattern element"); 224 if ("..".regionMatches(0, parametersPattern, start, firstElementEnd)) 225 return readElement(parametersPattern, firstComma + 1, startingWithAny()); 226 return readElement( 227 parametersPattern, firstComma + 1, 228 startingWithElement( 229 Types.parseExpression(parametersPattern.substring(0, firstElementEnd)))); 230 } 231 232 /*** 233 * Reads the next parameters pattern element from a string. 234 * 235 * @param parametersPattern The content of the pattern. 236 * @param index The index to start from. 237 * @param builder The Builder to use. 238 * 239 * @return A Parameters specification from the supplied pattern. 240 * 241 * @throws ParseException if the pattern is invalid. 242 */ 243 private static Parameters readElement ( 244 String parametersPattern, int index, Builder builder) { 245 int nextComma = parametersPattern.indexOf(',', index); 246 if (nextComma < 0) { 247 int elementStart = Expression.ltrim( 248 parametersPattern, index, parametersPattern.length()); 249 int elementEnd = Expression.rtrim( 250 parametersPattern, elementStart, parametersPattern.length()); 251 if (elementEnd == elementStart) 252 throw new ParseException("Invalid empty ending parameters pattern element"); 253 if ( 254 "..".regionMatches( 255 0, parametersPattern, elementStart, elementEnd - elementStart)) 256 return builder.endingWithAny(); 257 return builder.endingWithElement( 258 Types.parseExpression( 259 parametersPattern.substring(elementStart, elementEnd))); 260 } 261 int elementStart = Expression.ltrim( 262 parametersPattern, index, nextComma); 263 int elementEnd = Expression.rtrim( 264 parametersPattern, elementStart, nextComma); 265 if (elementEnd == elementStart) 266 throw new ParseException("Invalid empty parameters pattern element"); 267 if ( 268 "..".regionMatches( 269 0, parametersPattern, elementStart, elementEnd - elementStart)) 270 return readElement(parametersPattern, nextComma + 1, builder.followedByAny()); 271 return readElement( 272 parametersPattern, nextComma + 1, 273 builder.followedByElement( 274 Types.parseExpression( 275 parametersPattern.substring(elementStart, elementEnd)))); 276 } 277 278 /*** 279 * Returns true if the supplied parameter array is not null and satisfies this 280 * specification. 281 * 282 * @param paramTypes The parameter List to test. 283 * 284 * @return True if the supplied parameter array is not null and satisfies this 285 * specification. 286 */ 287 public final boolean isSatisfiedBy (Class[] paramTypes) { 288 return (paramTypes != null) && evaluate(paramTypes, 0); 289 } 290 291 /*** 292 * Returns true if the supplied parameter array satisfies this specification. 293 * 294 * @param paramTypes The parameter List to test. 295 * 296 * @return True if the supplied parameter array satisfies this specification. 297 */ 298 protected abstract boolean evaluate (Class[] paramTypes, int index); 299 300 /*** 301 * Returns true if all of the supplied Class arrays satisfy this specification. 302 * 303 * @param all The array of Class arrays to test. 304 * 305 * @return True if all of the supplied Class arrays satisfy this specification. 306 */ 307 public final boolean isSatisifiedByAll (Class[][] all) { 308 if (all == null) 309 throw new NullPointerException("all"); 310 for (int i = 0; i < all.length; ++i) 311 if (!isSatisfiedBy(all[i])) 312 return false; 313 return true; 314 } 315 316 /*** 317 * Returns true if any of the supplied Class arrays satisfy this specification. 318 * 319 * @param any The array of Class arrays to test. 320 * 321 * @return True if any of the supplied Class arrays satisfy this specification. 322 */ 323 public final boolean isSatisifiedByAny (Class[][] any) { 324 if (any == null) 325 throw new NullPointerException("any"); 326 for (int i = 0; i < any.length; ++i) 327 if (isSatisfiedBy(any[i])) 328 return true; 329 return false; 330 } 331 332 /*** 333 * Selects the first Class array that satisfies this specification from the 334 * supplied array. 335 * 336 * @param from The array of Class arrays to select from. 337 * 338 * @return The first Class arrays that satisfies this specification from the 339 * supplied array. 340 */ 341 public final Class[] selectFirst (Class[][] from) { 342 if (from == null) 343 throw new NullPointerException("from"); 344 for (int i = 0; i < from.length; ++i) 345 if (isSatisfiedBy(from[i])) 346 return from[i]; 347 return null; 348 } 349 350 /*** 351 * Selects all the Class arrays that satisfy this specification from the supplied 352 * array. 353 * 354 * @param from The array of Class arrays to select from. 355 * 356 * @return All the Class arrays that satisfy this specification from the supplied 357 * array. 358 */ 359 public final Class[][] selectAll (Class[][] from) { 360 if (from == null) 361 throw new NullPointerException("from"); 362 List results = new ArrayList(from.length); 363 for (int i = 0; i < from.length; ++i) 364 if (isSatisfiedBy(from[i])) 365 results.add(from[i]); 366 return (Class[][])results.toArray(new Class[results.size()][]); 367 } 368 369 /*** 370 * Returns a specification representing a logical AND of this specification on 371 * the left and the supplied specification on the right. 372 * 373 * @param specification The specification to AND with. 374 * 375 * @return A specification representing a logical AND of this specification on 376 * the left and the supplied specification on the right. 377 */ 378 public final Parameters and (final Parameters specification) { 379 if (specification == null) 380 throw new NullPointerException("specification"); 381 return new Parameters() { 382 protected boolean evaluate (Class[] paramTypes, int index) { 383 return Parameters.this.evaluate(paramTypes, index) 384 && specification.evaluate(paramTypes, index); 385 } 386 }; 387 } 388 389 /*** 390 * Returns a specification representing a logical OR of this specification on the 391 * left and the supplied specification on the right. 392 * 393 * @param specification The specification to OR with. 394 * 395 * @return A specification representing a logical OR of this specification on the 396 * left and the supplied specification on the right. 397 */ 398 public final Parameters or (final Parameters specification) { 399 if (specification == null) 400 throw new NullPointerException("specification"); 401 return new Parameters() { 402 protected boolean evaluate (Class[] paramTypes, int index) { 403 return Parameters.this.evaluate(paramTypes, index) 404 || specification.evaluate(paramTypes, index); 405 } 406 }; 407 } 408 409 /*** 410 * Returns a specification representing a logical NOT of this specification. 411 * 412 * @return A specification representing a logical NOT of this specification. 413 */ 414 public final Parameters not () { 415 return new Parameters() { 416 protected boolean evaluate (Class[] paramTypes, int index) { 417 return !Parameters.this.evaluate(paramTypes, index); 418 } 419 }; 420 } 421 422 /*** 423 * Helper class for sequentially constructing the elements of a common 424 * TokenizedStrings specification. 425 * 426 * @author Lonnie Pryor 427 * @version $Revision: 1.1 $ 428 */ 429 public abstract static class Builder { 430 /*** 431 * Returns a Builder whose resulting Parameters specification will allow any 432 * number of elements at this point in the sequence. 433 * 434 * @return A Builder whose resulting Parameters specification will allow any 435 * number of elements at this point in the sequence. 436 */ 437 public final Builder followedByAny () { 438 return new Builder() { 439 Parameters create (Parameters next) { 440 return Builder.this.create(newWildcard(next)); 441 } 442 }; 443 } 444 445 /*** 446 * Returns a Builder whose resulting Parameters specification will require the 447 * supplied element at this point in the sequence. 448 * 449 * @param specification The specification the element must match. 450 * 451 * @return A Builder whose resulting Parameters specification will require the 452 * supplied element at this point in the sequence. 453 */ 454 public final Builder followedByElement (Types specification) { 455 if (specification == null) 456 throw new NullPointerException("specification"); 457 return followedByElements(new Types[] { specification }); 458 } 459 460 /*** 461 * Returns a Builder whose resulting Parameters specification will require the 462 * supplied elements at this point in the sequence. 463 * 464 * @param specifications The specifications the elements must match. 465 * 466 * @return A Builder whose resulting Parameters specification will require the 467 * supplied elements at this point in the sequence. 468 */ 469 public final Builder followedByElements (final Types[] specifications) { 470 if (specifications == null) 471 throw new NullPointerException("specifications"); 472 return new Builder() { 473 Parameters create (Parameters next) { 474 return Builder.this.create(newElements(specifications, next)); 475 } 476 }; 477 } 478 479 /*** 480 * Returns a Parameters specification that will allow any number of elements at 481 * the end of the sequence. 482 * 483 * @return A Parameters specification that will allow any number of elements at 484 * the end of the sequence. 485 */ 486 public final Parameters endingWithAny () { 487 return Builder.this.create(newWildcard(null)); 488 } 489 490 /*** 491 * Returns a Parameters specification that will require the supplied element at 492 * the end of the sequence. 493 * 494 * @param specification The specification the element must match. 495 * 496 * @return A Parameters specification that will require the supplied element at 497 * the end of the sequence. 498 */ 499 public final Parameters endingWithElement (Types specification) { 500 if (specification == null) 501 throw new NullPointerException("specification"); 502 return endingWithElements(new Types[] { specification }); 503 } 504 505 /*** 506 * Returns a Parameters specification that will require the supplied elements 507 * at the end of the sequence. 508 * 509 * @param specifications The specifications the elements must match. 510 * 511 * @return A Parameters specification that will require the supplied elements 512 * at the end of the sequence. 513 */ 514 public final Parameters endingWithElements (final Types[] specifications) { 515 if (specifications == null) 516 throw new NullPointerException("specifications"); 517 return Builder.this.create(newElements(specifications, null)); 518 } 519 520 /*** 521 * Chained method for constructing instances of Parameters. 522 * 523 * @param next The next specification in the chain. 524 * 525 * @return A chained Parameters object for this Builder instance. 526 */ 527 abstract Parameters create (Parameters next); 528 529 /*** 530 * Creates a new chained Parameters instance that will match any number of 531 * elements. 532 * 533 * @param next The next specification in the chain. 534 * 535 * @return A new chained Parameters instance that will match any number of 536 * elements. 537 */ 538 static Parameters newWildcard (final Parameters next) { 539 return new Parameters() { 540 protected boolean evaluate (Class[] paramTypes, int index) { 541 if (next == null) 542 return true; 543 for (int i = index; i < paramTypes.length; ++i) 544 if (next.evaluate(paramTypes, i)) 545 return true; 546 return false; 547 } 548 }; 549 } 550 551 /*** 552 * Returns a new chained Parameters instance that will match the specified 553 * elements. 554 * 555 * @param specifications The specifications the elements must match. 556 * @param next The next specification in the chain. 557 * 558 * @return A new chained Parameters instance that will match the specified 559 * elements. 560 */ 561 static Parameters newElements ( 562 final Types[] specifications, final Parameters next) { 563 return new Parameters() { 564 protected boolean evaluate (Class[] paramTypes, int index) { 565 if ((paramTypes.length - index) < specifications.length) 566 return false; 567 for (int i = 0; i < specifications.length; i++) { 568 if (!specifications[i].isSatisfiedBy(paramTypes[index + i])) 569 return false; 570 } 571 if (next == null) 572 return (paramTypes.length - index) == specifications.length; 573 return next.evaluate(paramTypes, index + specifications.length); 574 } 575 }; 576 } 577 } 578 }

This page was automatically generated by Maven