KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > Tapestry


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry;
16
17 import java.io.IOException JavaDoc;
18 import java.io.InputStream JavaDoc;
19 import java.text.MessageFormat JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.HashMap JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.List JavaDoc;
25 import java.util.Locale JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Properties JavaDoc;
28 import java.util.ResourceBundle JavaDoc;
29 import java.util.Set JavaDoc;
30
31 import org.apache.hivemind.ApplicationRuntimeException;
32 import org.apache.hivemind.HiveMind;
33 import org.apache.hivemind.Location;
34 import org.apache.hivemind.service.ClassFabUtils;
35 import org.apache.tapestry.event.ChangeObserver;
36 import org.apache.tapestry.event.ObservedChangeEvent;
37 import org.apache.tapestry.services.ServiceConstants;
38 import org.apache.tapestry.spec.IComponentSpecification;
39 import org.apache.tapestry.util.StringSplitter;
40
41 /**
42  * A placeholder for a number of (static) methods that don't belong elsewhere, as well as a global
43  * location for static constants.
44  *
45  * @since 1.0.1
46  * @author Howard Lewis Ship
47  */

48
49 public final class Tapestry
50 {
51     /**
52      * The name ("action") of a service that allows behavior to be associated with an
53      * {@link IAction} component, such as {@link org.apache.tapestry.link.ActionLink }or
54      * {@link org.apache.tapestry.form.Form}.
55      * <p>
56      * This service is used with actions that are tied to the dynamic state of the page, and which
57      * require a rewind of the page.
58      */

59
60     public final static String JavaDoc ACTION_SERVICE = "action";
61
62     /**
63      * The name ("direct") of a service that allows stateless behavior for an {@link
64      * org.apache.tapestry.link.DirectLink} component.
65      * <p>
66      * This service rolls back the state of the page but doesn't rewind the the dynamic state of the
67      * page the was the action service does, which is more efficient but less powerful.
68      * <p>
69      * An array of String parameters may be included with the service URL; these will be made
70      * available to the {@link org.apache.tapestry.link.DirectLink} component's listener.
71      */

72
73     public final static String JavaDoc DIRECT_SERVICE = "direct";
74
75     /**
76      * The name ("external") of a service that a allows {@link IExternalPage} to be selected.
77      * Associated with a {@link org.apache.tapestry.link.ExternalLink} component.
78      * <p>
79      * This service enables {@link IExternalPage}s to be accessed via a URL. External pages may be
80      * booked marked using their URL for future reference.
81      * <p>
82      * An array of Object parameters may be included with the service URL; these will be passed to
83      * the {@link IExternalPage#activateExternalPage(Object[], IRequestCycle)} method.
84      */

85
86     public final static String JavaDoc EXTERNAL_SERVICE = "external";
87
88     /**
89      * The name ("page") of a service that allows a new page to be selected. Associated with a
90      * {@link org.apache.tapestry.link.PageLink} component.
91      * <p>
92      * The service requires a single parameter: the name of the target page.
93      */

94
95     public final static String JavaDoc PAGE_SERVICE = "page";
96
97     /**
98      * The name ("home") of a service that jumps to the home page. A stand-in for when no service is
99      * provided, which is typically the entrypoint to the application.
100      */

101
102     public final static String JavaDoc HOME_SERVICE = "home";
103
104     /**
105      * The name ("restart") of a service that invalidates the session and restarts the application.
106      * Typically used just to recover from an exception.
107      */

108
109     public static final String JavaDoc RESTART_SERVICE = "restart";
110
111     /**
112      * The name ("asset") of a service used to access internal assets.
113      */

114
115     public static final String JavaDoc ASSET_SERVICE = "asset";
116
117     /**
118      * The name ("reset") of a service used to clear cached template and specification data and
119      * remove all pooled pages. This is only used when debugging as a quick way to clear the out
120      * cached data, to allow updated versions of specifications and templates to be loaded (without
121      * stopping and restarting the servlet container).
122      * <p>
123      * This service is only available if the Java system property
124      * <code>org.apache.tapestry.enable-reset-service</code> is set to <code>true</code>.
125      */

126
127     public static final String JavaDoc RESET_SERVICE = "reset";
128
129     /**
130      * Query parameter that identfies the service for the request.
131      *
132      * @since 1.0.3
133      * @deprecated To be removed in 4.1. Use
134      * {@link org.apache.tapestry.services.ServiceConstants#SERVICE} instead.
135      */

136
137     public static final String JavaDoc SERVICE_QUERY_PARAMETER_NAME = ServiceConstants.SERVICE;
138
139     /**
140      * The query parameter for application specific parameters to the service (this is used with the
141      * direct service). Each of these values is encoded with
142      * {@link java.net.URLEncoder#encode(String)} before being added to the URL. Multiple values are
143      * handle by repeatedly establishing key/value pairs (this is a change from behavior in 2.1 and
144      * earlier).
145      *
146      * @since 1.0.3
147      * @deprecated To be removed in 4.1. Use
148      * {@link org.apache.tapestry.services.ServiceConstants#PARAMETER} instead.
149      */

150
151     public static final String JavaDoc PARAMETERS_QUERY_PARAMETER_NAME = ServiceConstants.PARAMETER;
152
153     /**
154      * Property name used to get the extension used for templates. This may be set in the page or
155      * component specification, or in the page (or component's) immediate container (library or
156      * application specification). Unlike most properties, value isn't inherited all the way up the
157      * chain. The default template extension is "html".
158      *
159      * @since 3.0
160      */

161
162     public static final String JavaDoc TEMPLATE_EXTENSION_PROPERTY = "org.apache.tapestry.template-extension";
163
164     /**
165      * The name of an {@link org.apache.tapestry.IRequestCycle} attribute in which the currently
166      * rendering {@link org.apache.tapestry.components.ILinkComponent} is stored. Link components do
167      * not nest.
168      */

169
170     public static final String JavaDoc LINK_COMPONENT_ATTRIBUTE_NAME = "org.apache.tapestry.active-link-component";
171
172     /**
173      * Suffix appended to a parameter name to form the name of a property that stores the binding
174      * for the parameter.
175      *
176      * @since 3.0
177      */

178
179     public static final String JavaDoc PARAMETER_PROPERTY_NAME_SUFFIX = "Binding";
180
181     /**
182      * Key used to obtain an extension from the application specification. The extension, if it
183      * exists, implements {@link org.apache.tapestry.request.IRequestDecoder}.
184      *
185      * @since 2.2
186      */

187
188     public static final String JavaDoc REQUEST_DECODER_EXTENSION_NAME = "org.apache.tapestry.request-decoder";
189
190     /**
191      * Name of optional application extension for the multipart decoder used by the application. The
192      * extension must implement {@link org.apache.tapestry.multipart.IMultipartDecoder} (and is
193      * generally a configured instance of
194      * {@link org.apache.tapestry.multipart.DefaultMultipartDecoder}).
195      *
196      * @since 3.0
197      */

198
199     public static final String JavaDoc MULTIPART_DECODER_EXTENSION_NAME = "org.apache.tapestry.multipart-decoder";
200
201     /**
202      * Method id used to check that {@link IPage#validate(IRequestCycle)} is invoked.
203      *
204      * @see #checkMethodInvocation(Object, String, Object)
205      * @since 3.0
206      */

207
208     public static final String JavaDoc ABSTRACTPAGE_VALIDATE_METHOD_ID = "AbstractPage.validate()";
209
210     /**
211      * Method id used to check that {@link IPage#detach()} is invoked.
212      *
213      * @see #checkMethodInvocation(Object, String, Object)
214      * @since 3.0
215      */

216
217     public static final String JavaDoc ABSTRACTPAGE_DETACH_METHOD_ID = "AbstractPage.detach()";
218
219     /**
220      * Regular expression defining a simple property name. Used by several different parsers. Simple
221      * property names match Java variable names; a leading letter (or underscore), followed by
222      * letters, numbers and underscores.
223      *
224      * @since 3.0
225      */

226
227     public static final String JavaDoc SIMPLE_PROPERTY_NAME_PATTERN = "^_?[a-zA-Z]\\w*$";
228
229     /**
230      * Name of an application extension used as a factory for
231      * {@link org.apache.tapestry.engine.IMonitor}instances. The extension must implement
232      * {@link org.apache.tapestry.engine.IMonitorFactory}.
233      *
234      * @since 3.0
235      */

236
237     public static final String JavaDoc MONITOR_FACTORY_EXTENSION_NAME = "org.apache.tapestry.monitor-factory";
238
239     /**
240      * Class name of an {@link ognl.TypeConverter}implementing class to use as a type converter for
241      * {@link org.apache.tapestry.binding.ExpressionBinding}
242      */

243     public static final String JavaDoc OGNL_TYPE_CONVERTER = "org.apache.tapestry.ognl-type-converter";
244
245     /**
246      * Prevent instantiation.
247      */

248
249     private Tapestry()
250     {
251     }
252
253     /**
254      * The version of the framework; this is updated for major releases.
255      */

256
257     public static final String JavaDoc VERSION = readVersion();
258
259     /**
260      * Contains strings loaded from TapestryStrings.properties.
261      *
262      * @since 1.0.8
263      */

264
265     private static ResourceBundle JavaDoc _strings;
266
267     /**
268      * A {@link Map}that links Locale names (as in {@link Locale#toString()}to {@link Locale}
269      * instances. This prevents needless duplication of Locales.
270      */

271
272     private static final Map JavaDoc _localeMap = new HashMap JavaDoc();
273
274     static
275     {
276         Locale JavaDoc[] locales = Locale.getAvailableLocales();
277         for (int i = 0; i < locales.length; i++)
278         {
279             _localeMap.put(locales[i].toString(), locales[i]);
280         }
281     }
282
283     /**
284      * Used for tracking if a particular super-class method has been invoked.
285      */

286
287     private static final ThreadLocal JavaDoc _invokedMethodIds = new ThreadLocal JavaDoc();
288
289     /**
290      * Copys all informal {@link IBinding bindings}from a source component to the destination
291      * component. Informal bindings are bindings for informal parameters. This will overwrite
292      * parameters (formal or informal) in the destination component if there is a naming conflict.
293      */

294
295     public static void copyInformalBindings(IComponent source, IComponent destination)
296     {
297         Collection JavaDoc names = source.getBindingNames();
298
299         if (names == null)
300             return;
301
302         IComponentSpecification specification = source.getSpecification();
303         Iterator JavaDoc i = names.iterator();
304
305         while (i.hasNext())
306         {
307             String JavaDoc name = (String JavaDoc) i.next();
308
309             // If not a formal parameter, then copy it over.
310

311             if (specification.getParameter(name) == null)
312             {
313                 IBinding binding = source.getBinding(name);
314
315                 destination.setBinding(name, binding);
316             }
317         }
318     }
319
320     /**
321      * Gets the {@link Locale}for the given string, which is the result of
322      * {@link Locale#toString()}. If no such locale is already registered, a new instance is
323      * created, registered and returned.
324      */

325
326     public static Locale JavaDoc getLocale(String JavaDoc s)
327     {
328         Locale JavaDoc result = null;
329
330         synchronized (_localeMap)
331         {
332             result = (Locale JavaDoc) _localeMap.get(s);
333         }
334
335         if (result == null)
336         {
337             StringSplitter splitter = new StringSplitter('_');
338             String JavaDoc[] terms = splitter.splitToArray(s);
339
340             switch (terms.length)
341             {
342                 case 1:
343
344                     result = new Locale JavaDoc(terms[0], "");
345                     break;
346
347                 case 2:
348
349                     result = new Locale JavaDoc(terms[0], terms[1]);
350                     break;
351
352                 case 3:
353
354                     result = new Locale JavaDoc(terms[0], terms[1], terms[2]);
355                     break;
356
357                 default:
358
359                     throw new IllegalArgumentException JavaDoc("Unable to convert '" + s + "' to a Locale.");
360             }
361
362             synchronized (_localeMap)
363             {
364                 _localeMap.put(s, result);
365             }
366
367         }
368
369         return result;
370
371     }
372
373     /**
374      * Closes the stream (if not null), ignoring any {@link IOException}thrown.
375      *
376      * @since 1.0.2
377      */

378
379     public static void close(InputStream JavaDoc stream)
380     {
381         if (stream != null)
382         {
383             try
384             {
385                 stream.close();
386             }
387             catch (IOException JavaDoc ex)
388             {
389                 // Ignore.
390
}
391         }
392     }
393
394     /**
395      * Gets a string from the TapestryStrings resource bundle. The string in the bundle is treated
396      * as a pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}.
397      *
398      * @since 1.0.8
399      */

400
401     public static String JavaDoc format(String JavaDoc key, Object JavaDoc[] args)
402     {
403         if (_strings == null)
404             _strings = ResourceBundle.getBundle("org.apache.tapestry.TapestryStrings");
405
406         String JavaDoc pattern = _strings.getString(key);
407
408         if (args == null)
409             return pattern;
410
411         return MessageFormat.format(pattern, args);
412     }
413
414     /**
415      * Convienience method for invoking {@link #format(String, Object[])}.
416      *
417      * @since 3.0
418      */

419
420     public static String JavaDoc getMessage(String JavaDoc key)
421     {
422         return format(key, null);
423     }
424
425     /**
426      * Convienience method for invoking {@link #format(String, Object[])}.
427      *
428      * @since 3.0
429      */

430
431     public static String JavaDoc format(String JavaDoc key, Object JavaDoc arg)
432     {
433         return format(key, new Object JavaDoc[]
434         { arg });
435     }
436
437     /**
438      * Convienience method for invoking {@link #format(String, Object[])}.
439      *
440      * @since 3.0
441      */

442
443     public static String JavaDoc format(String JavaDoc key, Object JavaDoc arg1, Object JavaDoc arg2)
444     {
445         return format(key, new Object JavaDoc[]
446         { arg1, arg2 });
447     }
448
449     /**
450      * Convienience method for invoking {@link #format(String, Object[])}.
451      *
452      * @since 3.0
453      */

454
455     public static String JavaDoc format(String JavaDoc key, Object JavaDoc arg1, Object JavaDoc arg2, Object JavaDoc arg3)
456     {
457         return format(key, new Object JavaDoc[]
458         { arg1, arg2, arg3 });
459     }
460
461     private static final String JavaDoc UNKNOWN_VERSION = "Unknown";
462
463     /**
464      * Invoked when the class is initialized to read the current version file.
465      */

466
467     private static final String JavaDoc readVersion()
468     {
469         Properties JavaDoc props = new Properties JavaDoc();
470
471         try
472         {
473             InputStream JavaDoc in = Tapestry.class.getResourceAsStream("version.properties");
474
475             if (in == null)
476                 return UNKNOWN_VERSION;
477
478             props.load(in);
479
480             in.close();
481
482             return props.getProperty("project.version", UNKNOWN_VERSION);
483         }
484         catch (IOException JavaDoc ex)
485         {
486             return UNKNOWN_VERSION;
487         }
488
489     }
490
491     /**
492      * Returns the size of a collection, or zero if the collection is null.
493      *
494      * @since 2.2
495      */

496
497     public static int size(Collection JavaDoc c)
498     {
499         if (c == null)
500             return 0;
501
502         return c.size();
503     }
504
505     /**
506      * Returns the length of the array, or 0 if the array is null.
507      *
508      * @since 2.2
509      */

510
511     public static int size(Object JavaDoc[] array)
512     {
513         if (array == null)
514             return 0;
515
516         return array.length;
517     }
518
519     /**
520      * Returns true if the Map is null or empty.
521      *
522      * @since 3.0
523      */

524
525     public static boolean isEmpty(Map JavaDoc map)
526     {
527         return map == null || map.isEmpty();
528     }
529
530     /**
531      * Returns true if the Collection is null or empty.
532      *
533      * @since 3.0
534      */

535
536     public static boolean isEmpty(Collection JavaDoc c)
537     {
538         return c == null || c.isEmpty();
539     }
540
541     /**
542      * Converts a {@link Map} to an even-sized array of key/value pairs. This may be useful when
543      * using a Map as service parameters (with {@link org.apache.tapestry.link.DirectLink}.
544      * Assuming the keys and values are simple objects (String, Boolean, Integer, etc.), then the
545      * representation as an array will encode more efficiently (via
546      * {@link org.apache.tapestry.util.io.DataSqueezerImpl} than serializing the Map and its
547      * contents.
548      *
549      * @return the array of keys and values, or null if the input Map is null or empty
550      * @since 2.2
551      */

552
553     public static Object JavaDoc[] convertMapToArray(Map JavaDoc map)
554     {
555         if (isEmpty(map))
556             return null;
557
558         Set JavaDoc entries = map.entrySet();
559
560         Object JavaDoc[] result = new Object JavaDoc[2 * entries.size()];
561         int x = 0;
562
563         Iterator JavaDoc i = entries.iterator();
564         while (i.hasNext())
565         {
566             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
567
568             result[x++] = entry.getKey();
569             result[x++] = entry.getValue();
570         }
571
572         return result;
573     }
574
575     /**
576      * Converts an even-sized array of objects back into a {@link Map}.
577      *
578      * @see #convertMapToArray(Map)
579      * @return a Map, or null if the array is null or empty
580      * @since 2.2
581      */

582
583     public static Map JavaDoc convertArrayToMap(Object JavaDoc[] array)
584     {
585         if (array == null || array.length == 0)
586             return null;
587
588         if (array.length % 2 != 0)
589             throw new IllegalArgumentException JavaDoc(getMessage("Tapestry.even-sized-array"));
590
591         Map JavaDoc result = new HashMap JavaDoc();
592
593         int x = 0;
594         while (x < array.length)
595         {
596             Object JavaDoc key = array[x++];
597             Object JavaDoc value = array[x++];
598
599             result.put(key, value);
600         }
601
602         return result;
603     }
604
605     /**
606      * Given a Class, creates a presentable name for the class, even if the class is a scalar type
607      * or Array type.
608      *
609      * @since 3.0
610      * @deprecated To be removed in 4.1.
611      */

612
613     public static String JavaDoc getClassName(Class JavaDoc subject)
614     {
615         return ClassFabUtils.getJavaClassName(subject);
616     }
617
618     /**
619      * Creates an exception indicating the binding value is null.
620      *
621      * @since 3.0
622      */

623
624     public static BindingException createNullBindingException(IBinding binding)
625     {
626         return new BindingException(getMessage("null-value-for-binding"), binding);
627     }
628
629     /** @since 3.0 * */
630
631     public static ApplicationRuntimeException createNoSuchComponentException(IComponent component,
632             String JavaDoc id, Location location)
633     {
634         return new ApplicationRuntimeException(format("no-such-component", component
635                 .getExtendedId(), id), component, location, null);
636     }
637
638     /** @since 3.0 * */
639
640     public static BindingException createRequiredParameterException(IComponent component,
641             String JavaDoc parameterName)
642     {
643         return new BindingException(format("required-parameter", parameterName, component
644                 .getExtendedId()), component, null, component.getBinding(parameterName), null);
645     }
646
647     /** @since 3.0 * */
648
649     public static ApplicationRuntimeException createRenderOnlyPropertyException(
650             IComponent component, String JavaDoc propertyName)
651     {
652         return new ApplicationRuntimeException(format(
653                 "render-only-property",
654                 propertyName,
655                 component.getExtendedId()), component, null, null);
656     }
657
658     /**
659      * Clears the list of method invocations.
660      *
661      * @see #checkMethodInvocation(Object, String, Object)
662      * @since 3.0
663      */

664
665     public static void clearMethodInvocations()
666     {
667         _invokedMethodIds.set(null);
668     }
669
670     /**
671      * Adds a method invocation to the list of invocations. This is done in a super-class
672      * implementations.
673      *
674      * @see #checkMethodInvocation(Object, String, Object)
675      * @since 3.0
676      */

677
678     public static void addMethodInvocation(Object JavaDoc methodId)
679     {
680         List JavaDoc methodIds = (List JavaDoc) _invokedMethodIds.get();
681
682         if (methodIds == null)
683         {
684             methodIds = new ArrayList JavaDoc();
685             _invokedMethodIds.set(methodIds);
686         }
687
688         methodIds.add(methodId);
689     }
690
691     /**
692      * Checks to see if a particular method has been invoked. The method is identified by a methodId
693      * (usually a String). The methodName and object are used to create an error message.
694      * <p>
695      * The caller should invoke {@link #clearMethodInvocations()}, then invoke a method on the
696      * object. The super-class implementation should invoke {@link #addMethodInvocation(Object)} to
697      * indicate that it was, in fact, invoked. The caller then invokes this method to validate that
698      * the super-class implementation was invoked.
699      * <p>
700      * The list of method invocations is stored in a {@link ThreadLocal} variable.
701      *
702      * @since 3.0
703      */

704
705     public static void checkMethodInvocation(Object JavaDoc methodId, String JavaDoc methodName, Object JavaDoc object)
706     {
707         List JavaDoc methodIds = (List JavaDoc) _invokedMethodIds.get();
708
709         if (methodIds != null && methodIds.contains(methodId))
710             return;
711
712         throw new ApplicationRuntimeException(Tapestry.format(
713                 "Tapestry.missing-method-invocation",
714                 object.getClass().getName(),
715                 methodName));
716     }
717
718     /**
719      * Method used by pages and components to send notifications about property changes.
720      *
721      * @param component
722      * the component containing the property
723      * @param propertyName
724      * the name of the property which changed
725      * @param newValue
726      * the new value for the property
727      * @since 3.0
728      */

729     public static void fireObservedChange(IComponent component, String JavaDoc propertyName, Object JavaDoc newValue)
730     {
731         ChangeObserver observer = component.getPage().getChangeObserver();
732
733         if (observer == null)
734             return;
735
736         ObservedChangeEvent event = new ObservedChangeEvent(component, propertyName, newValue);
737
738         observer.observeChange(event);
739     }
740
741     /**
742      * Returns true if the input is null or contains only whitespace.
743      * <p>
744      * Note: Yes, you'd think we'd use <code>StringUtils</code>, but with the change in names and
745      * behavior between releases, it is smarter to just implement our own little method!
746      *
747      * @since 3.0
748      * @deprecated To be removed in Tapestry 4.1. Use {@link HiveMind#isBlank(java.lang.String)}
749      * instead.
750      */

751
752     public static boolean isBlank(String JavaDoc input)
753     {
754         return HiveMind.isBlank(input);
755     }
756
757     /**
758      * Returns true if the input is not null and not empty (or only whitespace).
759      *
760      * @since 3.0
761      * @deprecated To be removed in Tapestry 4.1. Use {@link HiveMind#isNonBlank(java.lang.String)}
762      * instead.
763      */

764
765     public static boolean isNonBlank(String JavaDoc input)
766     {
767         return HiveMind.isNonBlank(input);
768     }
769 }
Popular Tags