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