| /* |
| * ==================================================================== |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 2002 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Apache" and "Apache Software Foundation" and |
| * "Apache Tapestry" must not be used to endorse or promote products |
| * derived from this software without prior written permission. For |
| * written permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * "Apache Tapestry", nor may "Apache" appear in their name, without |
| * prior written permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| package net.sf.tapestry; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.text.MessageFormat; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.ResourceBundle; |
| import java.util.Set; |
| |
| import net.sf.tapestry.spec.ComponentSpecification; |
| import net.sf.tapestry.util.AdaptorRegistry; |
| import net.sf.tapestry.util.StringSplitter; |
| |
| /** |
| * A placeholder for a number of (static) methods that don't belong elsewhere. |
| * |
| * @since 1.0.1 |
| * @version $Id$ |
| * @author Howard Lewis Ship |
| * |
| **/ |
| |
| public final class Tapestry |
| { |
| /** |
| * Prevent instantiation. |
| * |
| **/ |
| |
| private Tapestry() |
| { |
| } |
| |
| /** |
| * The version of the framework; this is updated for major releases. |
| * |
| **/ |
| |
| public static final String VERSION = readVersion(); |
| |
| /** |
| * Contains strings loaded from TapestryStrings.properties. |
| * |
| * @since 1.0.8 |
| * |
| **/ |
| |
| private static ResourceBundle _strings; |
| |
| /** |
| * A {@link Map} that links Locale names (as in {@link Locale#toString()} to |
| * {@link Locale} instances. This prevents needless duplication |
| * of Locales. |
| * |
| **/ |
| |
| private static final Map _localeMap = new HashMap(); |
| |
| static { |
| Locale[] locales = Locale.getAvailableLocales(); |
| for (int i = 0; i < locales.length; i++) |
| { |
| _localeMap.put(locales[i].toString(), locales[i]); |
| } |
| } |
| |
| /** |
| * A {@link net.sf.tapestry.util.AdaptorRegistry} used to coerce arbitrary objects |
| * to boolean values. |
| * |
| * @see #evaluateBoolean(Object) |
| **/ |
| |
| private static final AdaptorRegistry _booleanAdaptors = new AdaptorRegistry(); |
| |
| private static abstract class BoolAdaptor |
| { |
| /** |
| * Implemented by subclasses to coerce an object to a boolean. |
| * |
| **/ |
| |
| public abstract boolean coerce(Object value); |
| } |
| |
| private static class BooleanAdaptor extends BoolAdaptor |
| { |
| public boolean coerce(Object value) |
| { |
| Boolean b = (Boolean) value; |
| |
| return b.booleanValue(); |
| } |
| } |
| |
| private static class NumberAdaptor extends BoolAdaptor |
| { |
| public boolean coerce(Object value) |
| { |
| Number n = (Number) value; |
| |
| return n.intValue() > 0; |
| } |
| } |
| |
| private static class CollectionAdaptor extends BoolAdaptor |
| { |
| public boolean coerce(Object value) |
| { |
| Collection c = (Collection) value; |
| |
| return c.size() > 0; |
| } |
| } |
| |
| private static class StringAdaptor extends BoolAdaptor |
| { |
| public boolean coerce(Object value) |
| { |
| String s = (String) value; |
| |
| if (s.length() == 0) |
| return false; |
| |
| char[] data = s.toCharArray(); |
| |
| try |
| { |
| for (int i = 0;; i++) |
| { |
| char ch = data[i]; |
| if (!Character.isWhitespace(ch)) |
| return true; |
| } |
| } |
| catch (IndexOutOfBoundsException ex) |
| { |
| return false; |
| } |
| } |
| } |
| |
| static { |
| _booleanAdaptors.register(Boolean.class, new BooleanAdaptor()); |
| _booleanAdaptors.register(Number.class, new NumberAdaptor()); |
| _booleanAdaptors.register(Collection.class, new CollectionAdaptor()); |
| _booleanAdaptors.register(String.class, new StringAdaptor()); |
| |
| // Register a default, catch-all adaptor. |
| |
| _booleanAdaptors.register(Object.class, new BoolAdaptor() |
| { |
| public boolean coerce(Object value) |
| { |
| return true; |
| } |
| }); |
| } |
| |
| /** |
| * {@link AdaptorRegistry} used to extract an {@link Iterator} from |
| * an arbitrary object. |
| * |
| **/ |
| |
| private static AdaptorRegistry _iteratorAdaptors = new AdaptorRegistry(); |
| |
| private abstract static class IteratorAdaptor |
| { |
| /** |
| * Coeerces the object into an {@link Iterator}. |
| * |
| **/ |
| |
| abstract public Iterator coerce(Object value); |
| } |
| |
| static { |
| _iteratorAdaptors.register(Iterator.class, new IteratorAdaptor() |
| { |
| public Iterator coerce(Object value) |
| { |
| return (Iterator) value; |
| } |
| }); |
| |
| _iteratorAdaptors.register(Collection.class, new IteratorAdaptor() |
| { |
| public Iterator coerce(Object value) |
| { |
| Collection c = (Collection) value; |
| |
| if (c.size() == 0) |
| return null; |
| |
| return c.iterator(); |
| } |
| }); |
| |
| _iteratorAdaptors.register(Object.class, new IteratorAdaptor() |
| { |
| public Iterator coerce(Object value) |
| { |
| return Collections.singleton(value).iterator(); |
| } |
| }); |
| } |
| |
| /** |
| * Returns true if the value is null or empty (is the empty string, |
| * or contains only whitespace). |
| * |
| **/ |
| |
| public static boolean isNull(String value) |
| { |
| if (value == null) |
| return true; |
| |
| if (value.length() == 0) |
| return true; |
| |
| return value.trim().length() == 0; |
| } |
| |
| /** |
| * Copys all informal {@link IBinding bindings} from a source component |
| * to the destination component. Informal bindings are bindings for |
| * informal parameters. This will overwrite parameters (formal or |
| * informal) in the |
| * destination component if there is a naming conflict. |
| * |
| * |
| **/ |
| |
| public static void copyInformalBindings(IComponent source, IComponent destination) |
| { |
| Collection names = source.getBindingNames(); |
| |
| if (names == null) |
| return; |
| |
| ComponentSpecification specification = source.getSpecification(); |
| Iterator i = names.iterator(); |
| |
| while (i.hasNext()) |
| { |
| String name = (String) i.next(); |
| |
| // If not a formal parameter, then copy it over. |
| |
| if (specification.getParameter(name) == null) |
| { |
| IBinding binding = source.getBinding(name); |
| |
| destination.setBinding(name, binding); |
| } |
| } |
| } |
| |
| /** |
| * Evaluates an object to determine its boolean value. |
| * |
| * <table border=1> |
| * <tr> <th>Class</th> <th>Test</th> </tr> |
| * <tr> |
| * <td>{@link Boolean}</td> |
| * <td>Self explanatory.</td> |
| * </tr> |
| * <tr> <td>{@link Number}</td> |
| * <td>True if non-zero, false otherwise.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link Collection}</td> |
| * <td>True if contains any elements (non-zero size), false otherwise.</td> |
| * </tr> |
| * <tr> |
| * <td>{@link String}</td> |
| * <td>True if contains any non-whitespace characters, false otherwise.</td> |
| * </tr> |
| * <tr> |
| * <td>Any array type</td> |
| * <td>True if contains any elements (non-zero length), false otherwise.</td> |
| * <tr> |
| *</table> |
| * |
| * <p>Any other non-null object evaluates to true. |
| * |
| **/ |
| |
| public static boolean evaluateBoolean(Object value) |
| { |
| if (value == null) |
| return false; |
| |
| Class valueClass = value.getClass(); |
| if (valueClass.isArray()) |
| { |
| Object[] array = (Object[]) value; |
| |
| return array.length > 0; |
| } |
| |
| BoolAdaptor adaptor = (BoolAdaptor) _booleanAdaptors.getAdaptor(valueClass); |
| |
| return adaptor.coerce(value); |
| } |
| |
| /** |
| * Converts an Object into an {@link Iterator}, following some basic rules. |
| * |
| * <table border=1> |
| * <tr><th>Input Class</th> <th>Result</th> </tr> |
| * <tr><td>Object array</td> <td>Converted to a {@link List} and iterator returned. |
| * null returned if the array is empty.</td> |
| * </tr> |
| * <tr><td>{@link Iterator}</td> <td>Returned as-is.</td> |
| * <tr><td>{@link Collection}</td> <td>Iterator returned, or null |
| * if the Collection is empty</td> </tr> |
| * <tr><td>Any other</td> <td>{@link Iterator} for singleton collection returned</td> </tr> |
| * <tr><td>null</td> <td>null returned</td> </tr> |
| * </table> |
| * |
| **/ |
| |
| public static Iterator coerceToIterator(Object value) |
| { |
| if (value == null) |
| return null; |
| |
| Class valueClass = value.getClass(); |
| if (valueClass.isArray()) |
| { |
| Object[] array = (Object[]) value; |
| |
| if (array.length == 0) |
| return null; |
| |
| List l = Arrays.asList(array); |
| |
| return l.iterator(); |
| } |
| |
| IteratorAdaptor adaptor = (IteratorAdaptor) _iteratorAdaptors.getAdaptor(valueClass); |
| |
| return adaptor.coerce(value); |
| } |
| |
| /** |
| * Gets the {@link Locale} for the given string, which is the result |
| * of {@link Locale#toString()}. If no such locale is already registered, |
| * a new instance is created, registered and returned. |
| * |
| * |
| **/ |
| |
| public static Locale getLocale(String s) |
| { |
| Locale result = null; |
| |
| synchronized (_localeMap) |
| { |
| result = (Locale) _localeMap.get(s); |
| } |
| |
| if (result == null) |
| { |
| StringSplitter splitter = new StringSplitter('_'); |
| String[] terms = splitter.splitToArray(s); |
| |
| switch (terms.length) |
| { |
| case 1 : |
| |
| result = new Locale(terms[0], ""); |
| break; |
| |
| case 2 : |
| |
| result = new Locale(terms[0], terms[1]); |
| break; |
| |
| case 3 : |
| |
| result = new Locale(terms[0], terms[1], terms[2]); |
| break; |
| |
| default : |
| |
| throw new IllegalArgumentException("Unable to convert '" + s + "' to a Locale."); |
| } |
| |
| synchronized (_localeMap) |
| { |
| _localeMap.put(s, result); |
| } |
| |
| } |
| |
| return result; |
| |
| } |
| |
| /** |
| * Closes the stream (if not null), ignoring any {@link IOException} thrown. |
| * |
| * @since 1.0.2 |
| * |
| **/ |
| |
| public static void close(InputStream stream) |
| { |
| if (stream != null) |
| { |
| try |
| { |
| stream.close(); |
| } |
| catch (IOException ex) |
| { |
| // Ignore. |
| } |
| } |
| } |
| |
| /** |
| * Gets a string from the TapestryStrings resource bundle. |
| * The string in the bundle |
| * is treated as a pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}. |
| * |
| * @since 1.0.8 |
| * |
| **/ |
| |
| public static String getString(String key, Object[] args) |
| { |
| if (_strings == null) |
| _strings = ResourceBundle.getBundle("net.sf.tapestry.TapestryStrings"); |
| |
| String pattern = _strings.getString(key); |
| |
| if (args == null) |
| return pattern; |
| |
| return MessageFormat.format(pattern, args); |
| } |
| |
| /** |
| * Convienience method for invoking {@link #getString(String, Object[])}. |
| * |
| * @since 1.0.8 |
| **/ |
| |
| public static String getString(String key) |
| { |
| return getString(key, null); |
| } |
| |
| /** |
| * Convienience method for invoking {@link #getString(String, Object[])}. |
| * |
| * @since 1.0.8 |
| **/ |
| |
| public static String getString(String key, Object arg) |
| { |
| return getString(key, new Object[] { arg }); |
| } |
| |
| /** |
| * Convienience method for invoking {@link #getString(String, Object[])}. |
| * |
| * @since 1.0.8 |
| * |
| **/ |
| |
| public static String getString(String key, Object arg1, Object arg2) |
| { |
| return getString(key, new Object[] { arg1, arg2 }); |
| } |
| |
| /** |
| * Convienience method for invoking {@link #getString(String, Object[])}. |
| * |
| * @since 1.0.8 |
| * |
| **/ |
| |
| public static String getString(String key, Object arg1, Object arg2, Object arg3) |
| { |
| return getString(key, new Object[] { arg1, arg2, arg3 }); |
| } |
| |
| private static final String UNKNOWN_VERSION = "Unknown"; |
| |
| /** |
| * Invoked when the class is initialized to read the current version file. |
| * |
| **/ |
| |
| private static final String readVersion() |
| { |
| Properties props = new Properties(); |
| |
| try |
| { |
| InputStream in = Tapestry.class.getResourceAsStream("Version.properties"); |
| |
| if (in == null) |
| return UNKNOWN_VERSION; |
| |
| props.load(in); |
| |
| in.close(); |
| |
| return props.getProperty("framework.version", UNKNOWN_VERSION); |
| } |
| catch (IOException ex) |
| { |
| return UNKNOWN_VERSION; |
| } |
| |
| } |
| |
| /** |
| * Returns the size of a collection, or zero if the collection is null. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static int size(Collection c) |
| { |
| if (c == null) |
| return 0; |
| |
| return c.size(); |
| } |
| |
| /** |
| * Returns the length of the array, or 0 if the array is null. |
| * |
| * @since 2.2 |
| * |
| **/ |
| |
| public static int size(Object[] array) |
| { |
| if (array == null) |
| return 0; |
| |
| return array.length; |
| } |
| |
| /** |
| * Converts a {@link Map} to an even-sized array of key/value |
| * pairs. This may be useful when using a Map as service parameters |
| * (with {@link net.sf.tapestry.link.DirectLink}. Assuming the keys |
| * and values are simple objects (String, Boolean, Integer, etc.), then |
| * the representation as an array will encode more efficiently |
| * (via {@link net.sf.tapestry.util.io.DataSqueezer} than |
| * serializing the Map and its contents. |
| * |
| * @return the array of keys and values, or null if the input |
| * Map is null or empty |
| * |
| * @since 2.2 |
| **/ |
| |
| public static Object[] convertMapToArray(Map map) |
| { |
| if (map == null || map.isEmpty()) |
| return null; |
| |
| Set entries = map.entrySet(); |
| |
| Object[] result = new Object[2 * entries.size()]; |
| int x = 0; |
| |
| Iterator i = entries.iterator(); |
| while (i.hasNext()) |
| { |
| Map.Entry entry = (Map.Entry)i.next(); |
| |
| result[x++] = entry.getKey(); |
| result[x++] = entry.getValue(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Converts an even-sized array of objects back |
| * into a {@link Map}. |
| * |
| * @see #convertMapToArray(Map) |
| * @return a Map, or null if the array is null or empty |
| * @since 2.2 |
| * |
| **/ |
| |
| public static Map convertArrayToMap(Object[] array) |
| { |
| if (array == null || array.length == 0) |
| return null; |
| |
| if (array.length % 2 != 0) |
| throw new IllegalArgumentException( |
| getString("Tapestry.even-sized-array")); |
| |
| Map result = new HashMap(); |
| |
| int x = 0; |
| while (x < array.length) |
| { |
| Object key = array[x++]; |
| Object value = array[x++]; |
| |
| result.put(key, value); |
| } |
| |
| return result; |
| } |
| } |