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