Clover coverage report - Code Coverage for tapestry release 4.0-alpha-2
Coverage timestamp: Thu May 5 2005 09:57:44 EDT
file stats: LOC: 770   Methods: 27
NCLOC: 289   Classes: 1
30 day Evaluation Version distributed via the Maven Jar Repository. Clover is not free. You have 30 days to evaluate it. Please visit http://www.thecortex.net/clover to obtain a licensed version of Clover
 
 Source file Conditionals Statements Methods TOTAL
Tapestry.java 62.5% 55.7% 63% 58.4%
coverage coverage
 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;
 18   
 import java.io.InputStream;
 19   
 import java.text.MessageFormat;
 20   
 import java.util.ArrayList;
 21   
 import java.util.Collection;
 22   
 import java.util.HashMap;
 23   
 import java.util.Iterator;
 24   
 import java.util.List;
 25   
 import java.util.Locale;
 26   
 import java.util.Map;
 27   
 import java.util.Properties;
 28   
 import java.util.ResourceBundle;
 29   
 import java.util.Set;
 30   
 
 31   
 import org.apache.hivemind.ApplicationRuntimeException;
 32   
 import org.apache.hivemind.HiveMind;
 33   
 import org.apache.hivemind.Location;
 34   
 import org.apache.tapestry.event.ChangeObserver;
 35   
 import org.apache.tapestry.event.ObservedChangeEvent;
 36   
 import org.apache.tapestry.services.ServiceConstants;
 37   
 import org.apache.tapestry.spec.IComponentSpecification;
 38   
 import org.apache.tapestry.util.StringSplitter;
 39   
 
 40   
 /**
 41   
  * A placeholder for a number of (static) methods that don't belong elsewhere, as well as a global
 42   
  * location for static constants.
 43   
  * 
 44   
  * @since 1.0.1
 45   
  * @author Howard Lewis Ship
 46   
  */
 47   
 
 48   
 public final class Tapestry
 49   
 {
 50   
     /**
 51   
      * The name ("action") of a service that allows behavior to be associated with an
 52   
      * {@link IAction}component, such as {@link org.apache.tapestry.link.ActionLink}or
 53   
      * {@link org.apache.tapestry.form.Form}.
 54   
      * <p>
 55   
      * This service is used with actions that are tied to the dynamic state of the page, and which
 56   
      * require a rewind of the page.
 57   
      */
 58   
 
 59   
     public final static String ACTION_SERVICE = "action";
 60   
 
 61   
     /**
 62   
      * The name ("direct") of a service that allows stateless behavior for an {@link
 63   
      * org.apache.tapestry.link.DirectLink} component.
 64   
      * <p>
 65   
      * This service rolls back the state of the page but doesn't rewind the the dynamic state of the
 66   
      * page the was the action service does, which is more efficient but less powerful.
 67   
      * <p>
 68   
      * An array of String parameters may be included with the service URL; these will be made
 69   
      * available to the {@link org.apache.tapestry.link.DirectLink}component's listener.
 70   
      */
 71   
 
 72   
     public final static String DIRECT_SERVICE = "direct";
 73   
 
 74   
     /**
 75   
      * The name ("external") of a service that a allows {@link IExternalPage}to be selected.
 76   
      * Associated with a {@link org.apache.tapestry.link.ExternalLink}component.
 77   
      * <p>
 78   
      * This service enables {@link IExternalPage}s to be accessed via a URL. External pages may be
 79   
      * booked marked using their URL for future reference.
 80   
      * <p>
 81   
      * An array of Object parameters may be included with the service URL; these will be passed to
 82   
      * the {@link IExternalPage#activateExternalPage(Object[], IRequestCycle)}method.
 83   
      */
 84   
 
 85   
     public final static String EXTERNAL_SERVICE = "external";
 86   
 
 87   
     /**
 88   
      * The name ("page") of a service that allows a new page to be selected. Associated with a
 89   
      * {@link org.apache.tapestry.link.PageLink}component.
 90   
      * <p>
 91   
      * The service requires a single parameter: the name of the target page.
 92   
      */
 93   
 
 94   
     public final static String PAGE_SERVICE = "page";
 95   
 
 96   
     /**
 97   
      * The name ("home") of a service that jumps to the home page. A stand-in for when no service is
 98   
      * provided, which is typically the entrypoint to the application.
 99   
      */
 100   
 
 101   
     public final static String HOME_SERVICE = "home";
 102   
 
 103   
     /**
 104   
      * The name ("restart") of a service that invalidates the session and restarts the application.
 105   
      * Typically used just to recover from an exception.
 106   
      */
 107   
 
 108   
     public static final String RESTART_SERVICE = "restart";
 109   
 
 110   
     /**
 111   
      * The name ("asset") of a service used to access internal assets.
 112   
      */
 113   
 
 114   
     public static final String ASSET_SERVICE = "asset";
 115   
 
 116   
     /**
 117   
      * The name ("reset") of a service used to clear cached template and specification data and
 118   
      * remove all pooled pages. This is only used when debugging as a quick way to clear the out
 119   
      * cached data, to allow updated versions of specifications and templates to be loaded (without
 120   
      * stopping and restarting the servlet container).
 121   
      * <p>
 122   
      * This service is only available if the Java system property
 123   
      * <code>org.apache.tapestry.enable-reset-service</code> is set to <code>true</code>.
 124   
      */
 125   
 
 126   
     public static final String RESET_SERVICE = "reset";
 127   
 
 128   
     /**
 129   
      * Query parameter that identfies the service for the request.
 130   
      * 
 131   
      * @since 1.0.3
 132   
      * @deprecated To be removed in 4.1. Use
 133   
      *             {@link org.apache.tapestry.services.ServiceConstants#SERVICE}instead.
 134   
      */
 135   
 
 136   
     public static final String SERVICE_QUERY_PARAMETER_NAME = ServiceConstants.SERVICE;
 137   
 
 138   
     /**
 139   
      * The query parameter for application specific parameters to the service (this is used with the
 140   
      * direct service). Each of these values is encoded with
 141   
      * {@link java.net.URLEncoder#encode(String)}before being added to the URL. Multiple values are
 142   
      * handle by repeatedly establishing key/value pairs (this is a change from behavior in 2.1 and
 143   
      * earlier).
 144   
      * 
 145   
      * @since 1.0.3
 146   
      * @deprecated To be removed in 4.1. Use
 147   
      *             {@link org.apache.tapestry.services.ServiceConstants#PARAMETER}instead.
 148   
      */
 149   
 
 150   
     public static final String PARAMETERS_QUERY_PARAMETER_NAME = ServiceConstants.PARAMETER;
 151   
 
 152   
     /**
 153   
      * Property name used to get the extension used for templates. This may be set in the page or
 154   
      * component specification, or in the page (or component's) immediate container (library or
 155   
      * application specification). Unlike most properties, value isn't inherited all the way up the
 156   
      * chain. The default template extension is "html".
 157   
      * 
 158   
      * @since 3.0
 159   
      */
 160   
 
 161   
     public static final String TEMPLATE_EXTENSION_PROPERTY = "org.apache.tapestry.template-extension";
 162   
 
 163   
     /**
 164   
      * The name of an {@link org.apache.tapestry.IRequestCycle}attribute in which the currently
 165   
      * rendering {@link org.apache.tapestry.components.ILinkComponent}is stored. Link components do
 166   
      * not nest.
 167   
      */
 168   
 
 169   
     public static final String LINK_COMPONENT_ATTRIBUTE_NAME = "org.apache.tapestry.active-link-component";
 170   
 
 171   
     /**
 172   
      * Suffix appended to a parameter name to form the name of a property that stores the binding
 173   
      * for the parameter.
 174   
      * 
 175   
      * @since 3.0
 176   
      */
 177   
 
 178   
     public static final String PARAMETER_PROPERTY_NAME_SUFFIX = "Binding";
 179   
 
 180   
     /**
 181   
      * Key used to obtain an extension from the application specification. The extension, if it
 182   
      * exists, implements {@link org.apache.tapestry.request.IRequestDecoder}.
 183   
      * 
 184   
      * @since 2.2
 185   
      */
 186   
 
 187   
     public static final String REQUEST_DECODER_EXTENSION_NAME = "org.apache.tapestry.request-decoder";
 188   
 
 189   
     /**
 190   
      * Name of optional application extension for the multipart decoder used by the application. The
 191   
      * extension must implement {@link org.apache.tapestry.multipart.IMultipartDecoder}(and is
 192   
      * generally a configured instance of
 193   
      * {@link org.apache.tapestry.multipart.DefaultMultipartDecoder}).
 194   
      * 
 195   
      * @since 3.0
 196   
      */
 197   
 
 198   
     public static final String MULTIPART_DECODER_EXTENSION_NAME = "org.apache.tapestry.multipart-decoder";
 199   
 
 200   
     /**
 201   
      * Method id used to check that {@link IPage#validate(IRequestCycle)}is invoked.
 202   
      * 
 203   
      * @see #checkMethodInvocation(Object, String, Object)
 204   
      * @since 3.0
 205   
      */
 206   
 
 207   
     public static final String ABSTRACTPAGE_VALIDATE_METHOD_ID = "AbstractPage.validate()";
 208   
 
 209   
     /**
 210   
      * Method id used to check that {@link IPage#detach()}is invoked.
 211   
      * 
 212   
      * @see #checkMethodInvocation(Object, String, Object)
 213   
      * @since 3.0
 214   
      */
 215   
 
 216   
     public static final String ABSTRACTPAGE_DETACH_METHOD_ID = "AbstractPage.detach()";
 217   
 
 218   
     /**
 219   
      * Regular expression defining a simple property name. Used by several different parsers. Simple
 220   
      * property names match Java variable names; a leading letter (or underscore), followed by
 221   
      * letters, numbers and underscores.
 222   
      * 
 223   
      * @since 3.0
 224   
      */
 225   
 
 226   
     public static final String SIMPLE_PROPERTY_NAME_PATTERN = "^_?[a-zA-Z]\\w*$";
 227   
 
 228   
     /**
 229   
      * Name of an application extension used as a factory for
 230   
      * {@link org.apache.tapestry.engine.IMonitor}instances. The extension must implement
 231   
      * {@link org.apache.tapestry.engine.IMonitorFactory}.
 232   
      * 
 233   
      * @since 3.0
 234   
      */
 235   
 
 236   
     public static final String MONITOR_FACTORY_EXTENSION_NAME = "org.apache.tapestry.monitor-factory";
 237   
 
 238   
     /**
 239   
      * Class name of an {@link ognl.TypeConverter}implementing class to use as a type converter for
 240   
      * {@link org.apache.tapestry.binding.ExpressionBinding}
 241   
      */
 242   
     public static final String OGNL_TYPE_CONVERTER = "org.apache.tapestry.ognl-type-converter";
 243   
 
 244   
     /**
 245   
      * Prevent instantiation.
 246   
      */
 247   
 
 248  0
     private Tapestry()
 249   
     {
 250   
     }
 251   
 
 252   
     /**
 253   
      * The version of the framework; this is updated for major releases.
 254   
      */
 255   
 
 256   
     public static final String VERSION = readVersion();
 257   
 
 258   
     /**
 259   
      * Contains strings loaded from TapestryStrings.properties.
 260   
      * 
 261   
      * @since 1.0.8
 262   
      */
 263   
 
 264   
     private static ResourceBundle _strings;
 265   
 
 266   
     /**
 267   
      * A {@link Map}that links Locale names (as in {@link Locale#toString()}to {@link Locale}
 268   
      * instances. This prevents needless duplication of Locales.
 269   
      */
 270   
 
 271   
     private static final Map _localeMap = new HashMap();
 272   
 
 273   
     static
 274   
     {
 275  1
         Locale[] locales = Locale.getAvailableLocales();
 276  1
         for (int i = 0; i < locales.length; i++)
 277   
         {
 278  134
             _localeMap.put(locales[i].toString(), locales[i]);
 279   
         }
 280   
     }
 281   
 
 282   
     /**
 283   
      * Used for tracking if a particular super-class method has been invoked.
 284   
      */
 285   
 
 286   
     private static final ThreadLocal _invokedMethodIds = new ThreadLocal();
 287   
 
 288   
     /**
 289   
      * Copys all informal {@link IBinding bindings}from a source component to the destination
 290   
      * component. Informal bindings are bindings for informal parameters. This will overwrite
 291   
      * parameters (formal or informal) in the destination component if there is a naming conflict.
 292   
      */
 293   
 
 294  0
     public static void copyInformalBindings(IComponent source, IComponent destination)
 295   
     {
 296  0
         Collection names = source.getBindingNames();
 297   
 
 298  0
         if (names == null)
 299  0
             return;
 300   
 
 301  0
         IComponentSpecification specification = source.getSpecification();
 302  0
         Iterator i = names.iterator();
 303   
 
 304  0
         while (i.hasNext())
 305   
         {
 306  0
             String name = (String) i.next();
 307   
 
 308   
             // If not a formal parameter, then copy it over.
 309   
 
 310  0
             if (specification.getParameter(name) == null)
 311   
             {
 312  0
                 IBinding binding = source.getBinding(name);
 313   
 
 314  0
                 destination.setBinding(name, binding);
 315   
             }
 316   
         }
 317   
     }
 318   
 
 319   
     /**
 320   
      * Gets the {@link Locale}for the given string, which is the result of
 321   
      * {@link Locale#toString()}. If no such locale is already registered, a new instance is
 322   
      * created, registered and returned.
 323   
      */
 324   
 
 325  0
     public static Locale getLocale(String s)
 326   
     {
 327  0
         Locale result = null;
 328   
 
 329  0
         synchronized (_localeMap)
 330   
         {
 331  0
             result = (Locale) _localeMap.get(s);
 332   
         }
 333   
 
 334  0
         if (result == null)
 335   
         {
 336  0
             StringSplitter splitter = new StringSplitter('_');
 337  0
             String[] terms = splitter.splitToArray(s);
 338   
 
 339  0
             switch (terms.length)
 340   
             {
 341   
                 case 1:
 342   
 
 343  0
                     result = new Locale(terms[0], "");
 344  0
                     break;
 345   
 
 346   
                 case 2:
 347   
 
 348  0
                     result = new Locale(terms[0], terms[1]);
 349  0
                     break;
 350   
 
 351   
                 case 3:
 352   
 
 353  0
                     result = new Locale(terms[0], terms[1], terms[2]);
 354  0
                     break;
 355   
 
 356   
                 default:
 357   
 
 358  0
                     throw new IllegalArgumentException("Unable to convert '" + s + "' to a Locale.");
 359   
             }
 360   
 
 361  0
             synchronized (_localeMap)
 362   
             {
 363  0
                 _localeMap.put(s, result);
 364   
             }
 365   
 
 366   
         }
 367   
 
 368  0
         return result;
 369   
 
 370   
     }
 371   
 
 372   
     /**
 373   
      * Closes the stream (if not null), ignoring any {@link IOException}thrown.
 374   
      * 
 375   
      * @since 1.0.2
 376   
      */
 377   
 
 378  175
     public static void close(InputStream stream)
 379   
     {
 380  175
         if (stream != null)
 381   
         {
 382  175
             try
 383   
             {
 384  175
                 stream.close();
 385   
             }
 386   
             catch (IOException ex)
 387   
             {
 388   
                 // Ignore.
 389   
             }
 390   
         }
 391   
     }
 392   
 
 393   
     /**
 394   
      * Gets a string from the TapestryStrings resource bundle. The string in the bundle is treated
 395   
      * as a pattern for {@link MessageFormat#format(java.lang.String, java.lang.Object[])}.
 396   
      * 
 397   
      * @since 1.0.8
 398   
      */
 399   
 
 400  30
     public static String format(String key, Object[] args)
 401   
     {
 402  30
         if (_strings == null)
 403  1
             _strings = ResourceBundle.getBundle("org.apache.tapestry.TapestryStrings");
 404   
 
 405  30
         String pattern = _strings.getString(key);
 406   
 
 407  30
         if (args == null)
 408  13
             return pattern;
 409   
 
 410  17
         return MessageFormat.format(pattern, args);
 411   
     }
 412   
 
 413   
     /**
 414   
      * Convienience method for invoking {@link #format(String, Object[])}.
 415   
      * 
 416   
      * @since 3.0
 417   
      */
 418   
 
 419  13
     public static String getMessage(String key)
 420   
     {
 421  13
         return format(key, null);
 422   
     }
 423   
 
 424   
     /**
 425   
      * Convienience method for invoking {@link #format(String, Object[])}.
 426   
      * 
 427   
      * @since 3.0
 428   
      */
 429   
 
 430  5
     public static String format(String key, Object arg)
 431   
     {
 432  5
         return format(key, new Object[]
 433   
         { arg });
 434   
     }
 435   
 
 436   
     /**
 437   
      * Convienience method for invoking {@link #format(String, Object[])}.
 438   
      * 
 439   
      * @since 3.0
 440   
      */
 441   
 
 442  9
     public static String format(String key, Object arg1, Object arg2)
 443   
     {
 444  9
         return format(key, new Object[]
 445   
         { arg1, arg2 });
 446   
     }
 447   
 
 448   
     /**
 449   
      * Convienience method for invoking {@link #format(String, Object[])}.
 450   
      * 
 451   
      * @since 3.0
 452   
      */
 453   
 
 454  3
     public static String format(String key, Object arg1, Object arg2, Object arg3)
 455   
     {
 456  3
         return format(key, new Object[]
 457   
         { arg1, arg2, arg3 });
 458   
     }
 459   
 
 460   
     private static final String UNKNOWN_VERSION = "Unknown";
 461   
 
 462   
     /**
 463   
      * Invoked when the class is initialized to read the current version file.
 464   
      */
 465   
 
 466  1
     private static final String readVersion()
 467   
     {
 468  1
         Properties props = new Properties();
 469   
 
 470  1
         try
 471   
         {
 472  1
             InputStream in = Tapestry.class.getResourceAsStream("version.properties");
 473   
 
 474  1
             if (in == null)
 475  0
                 return UNKNOWN_VERSION;
 476   
 
 477  1
             props.load(in);
 478   
 
 479  1
             in.close();
 480   
 
 481  1
             return props.getProperty("project.version", UNKNOWN_VERSION);
 482   
         }
 483   
         catch (IOException ex)
 484   
         {
 485  0
             return UNKNOWN_VERSION;
 486   
         }
 487   
 
 488   
     }
 489   
 
 490   
     /**
 491   
      * Returns the size of a collection, or zero if the collection is null.
 492   
      * 
 493   
      * @since 2.2
 494   
      */
 495   
 
 496  2318
     public static int size(Collection c)
 497   
     {
 498  2318
         if (c == null)
 499  1
             return 0;
 500   
 
 501  2317
         return c.size();
 502   
     }
 503   
 
 504   
     /**
 505   
      * Returns the length of the array, or 0 if the array is null.
 506   
      * 
 507   
      * @since 2.2
 508   
      */
 509   
 
 510  339
     public static int size(Object[] array)
 511   
     {
 512  339
         if (array == null)
 513  58
             return 0;
 514   
 
 515  281
         return array.length;
 516   
     }
 517   
 
 518   
     /**
 519   
      * Returns true if the Map is null or empty.
 520   
      * 
 521   
      * @since 3.0
 522   
      */
 523   
 
 524  0
     public static boolean isEmpty(Map map)
 525   
     {
 526  0
         return map == null || map.isEmpty();
 527   
     }
 528   
 
 529   
     /**
 530   
      * Returns true if the Collection is null or empty.
 531   
      * 
 532   
      * @since 3.0
 533   
      */
 534   
 
 535  90
     public static boolean isEmpty(Collection c)
 536   
     {
 537  90
         return c == null || c.isEmpty();
 538   
     }
 539   
 
 540   
     /**
 541   
      * Converts a {@link Map}to an even-sized array of key/value pairs. This may be useful when
 542   
      * using a Map as service parameters (with {@link org.apache.tapestry.link.DirectLink}.
 543   
      * Assuming the keys and values are simple objects (String, Boolean, Integer, etc.), then the
 544   
      * representation as an array will encode more efficiently (via
 545   
      * {@link org.apache.tapestry.util.io.DataSqueezerImpl}than serializing the Map and its
 546   
      * contents.
 547   
      * 
 548   
      * @return the array of keys and values, or null if the input Map is null or empty
 549   
      * @since 2.2
 550   
      */
 551   
 
 552  0
     public static Object[] convertMapToArray(Map map)
 553   
     {
 554  0
         if (isEmpty(map))
 555  0
             return null;
 556   
 
 557  0
         Set entries = map.entrySet();
 558   
 
 559  0
         Object[] result = new Object[2 * entries.size()];
 560  0
         int x = 0;
 561   
 
 562  0
         Iterator i = entries.iterator();
 563  0
         while (i.hasNext())
 564   
         {
 565  0
             Map.Entry entry = (Map.Entry) i.next();
 566   
 
 567  0
             result[x++] = entry.getKey();
 568  0
             result[x++] = entry.getValue();
 569   
         }
 570   
 
 571  0
         return result;
 572   
     }
 573   
 
 574   
     /**
 575   
      * Converts an even-sized array of objects back into a {@link Map}.
 576   
      * 
 577   
      * @see #convertMapToArray(Map)
 578   
      * @return a Map, or null if the array is null or empty
 579   
      * @since 2.2
 580   
      */
 581   
 
 582  24
     public static Map convertArrayToMap(Object[] array)
 583   
     {
 584  24
         if (array == null || array.length == 0)
 585  4
             return null;
 586   
 
 587  20
         if (array.length % 2 != 0)
 588  0
             throw new IllegalArgumentException(getMessage("Tapestry.even-sized-array"));
 589   
 
 590  20
         Map result = new HashMap();
 591   
 
 592  20
         int x = 0;
 593  20
         while (x < array.length)
 594   
         {
 595  37
             Object key = array[x++];
 596  37
             Object value = array[x++];
 597   
 
 598  37
             result.put(key, value);
 599   
         }
 600   
 
 601  20
         return result;
 602   
     }
 603   
 
 604   
     /**
 605   
      * Given a Class, creates a presentable name for the class, even if the class is a scalar type
 606   
      * or Array type.
 607   
      * 
 608   
      * @since 3.0
 609   
      */
 610   
 
 611  11
     public static String getClassName(Class subject)
 612   
     {
 613  11
         if (subject.isArray())
 614  5
             return getClassName(subject.getComponentType()) + "[]";
 615   
 
 616  6
         return subject.getName();
 617   
     }
 618   
 
 619   
     /**
 620   
      * Creates an exception indicating the binding value is null.
 621   
      * 
 622   
      * @since 3.0
 623   
      */
 624   
 
 625  0
     public static BindingException createNullBindingException(IBinding binding)
 626   
     {
 627  0
         return new BindingException(getMessage("null-value-for-binding"), binding);
 628   
     }
 629   
 
 630   
     /** @since 3.0 * */
 631   
 
 632  0
     public static ApplicationRuntimeException createNoSuchComponentException(IComponent component,
 633   
             String id, Location location)
 634   
     {
 635  0
         return new ApplicationRuntimeException(format("no-such-component", component
 636   
                 .getExtendedId(), id), component, location, null);
 637   
     }
 638   
 
 639   
     /** @since 3.0 * */
 640   
 
 641  1
     public static BindingException createRequiredParameterException(IComponent component,
 642   
             String parameterName)
 643   
     {
 644  1
         return new BindingException(format("required-parameter", parameterName, component
 645   
                 .getExtendedId()), component, null, component.getBinding(parameterName), null);
 646   
     }
 647   
 
 648   
     /** @since 3.0 * */
 649   
 
 650  0
     public static ApplicationRuntimeException createRenderOnlyPropertyException(
 651   
             IComponent component, String propertyName)
 652   
     {
 653  0
         return new ApplicationRuntimeException(format(
 654   
                 "render-only-property",
 655   
                 propertyName,
 656   
                 component.getExtendedId()), component, null, null);
 657   
     }
 658   
 
 659   
     /**
 660   
      * Clears the list of method invocations.
 661   
      * 
 662   
      * @see #checkMethodInvocation(Object, String, Object)
 663   
      * @since 3.0
 664   
      */
 665   
 
 666  424
     public static void clearMethodInvocations()
 667   
     {
 668  424
         _invokedMethodIds.set(null);
 669   
     }
 670   
 
 671   
     /**
 672   
      * Adds a method invocation to the list of invocations. This is done in a super-class
 673   
      * implementations.
 674   
      * 
 675   
      * @see #checkMethodInvocation(Object, String, Object)
 676   
      * @since 3.0
 677   
      */
 678   
 
 679  430
     public static void addMethodInvocation(Object methodId)
 680   
     {
 681  430
         List methodIds = (List) _invokedMethodIds.get();
 682   
 
 683  430
         if (methodIds == null)
 684   
         {
 685  424
             methodIds = new ArrayList();
 686  424
             _invokedMethodIds.set(methodIds);
 687   
         }
 688   
 
 689  430
         methodIds.add(methodId);
 690   
     }
 691   
 
 692   
     /**
 693   
      * Checks to see if a particular method has been invoked. The method is identified by a methodId
 694   
      * (usually a String). The methodName and object are used to create an error message.
 695   
      * <p>
 696   
      * The caller should invoke {@link #clearMethodInvocations()}, then invoke a method on the
 697   
      * object. The super-class implementation should invoke {@link #addMethodInvocation(Object)}to
 698   
      * indicate that it was, in fact, invoked. The caller then invokes this method to vlaidate that
 699   
      * the super-class implementation was invoked.
 700   
      * <p>
 701   
      * The list of method invocations is stored in a {@link ThreadLocal}variable.
 702   
      * 
 703   
      * @since 3.0
 704   
      */
 705   
 
 706  420
     public static void checkMethodInvocation(Object methodId, String methodName, Object object)
 707   
     {
 708  420
         List methodIds = (List) _invokedMethodIds.get();
 709   
 
 710  420
         if (methodIds != null && methodIds.contains(methodId))
 711  419
             return;
 712   
 
 713  1
         throw new ApplicationRuntimeException(Tapestry.format(
 714   
                 "Tapestry.missing-method-invocation",
 715   
                 object.getClass().getName(),
 716   
                 methodName));
 717   
     }
 718   
 
 719   
     /**
 720   
      * Method used by pages and components to send notifications about property changes.
 721   
      * 
 722   
      * @param component
 723   
      *            the component containing the property
 724   
      * @param propertyName
 725   
      *            the name of the property which changed
 726   
      * @param newValue
 727   
      *            the new value for the property
 728   
      * @since 3.0
 729   
      */
 730  52
     public static void fireObservedChange(IComponent component, String propertyName, Object newValue)
 731   
     {
 732  52
         ChangeObserver observer = component.getPage().getChangeObserver();
 733   
 
 734  52
         if (observer == null)
 735  22
             return;
 736   
 
 737  30
         ObservedChangeEvent event = new ObservedChangeEvent(component, propertyName, newValue);
 738   
 
 739  30
         observer.observeChange(event);
 740   
     }
 741   
 
 742   
     /**
 743   
      * Returns true if the input is null or contains only whitespace.
 744   
      * <p>
 745   
      * Note: Yes, you'd think we'd use <code>StringUtils</code>, but with the change in names and
 746   
      * behavior between releases, it is smarter to just implement our own little method!
 747   
      * 
 748   
      * @since 3.0
 749   
      * @deprecated To be removed in Tapestry 4.1. Use {@link HiveMind#isBlank(java.lang.String)}
 750   
      *             instead.
 751   
      */
 752   
 
 753  0
     public static boolean isBlank(String input)
 754   
     {
 755  0
         return HiveMind.isBlank(input);
 756   
     }
 757   
 
 758   
     /**
 759   
      * Returns true if the input is not null and not empty (or only whitespace).
 760   
      * 
 761   
      * @since 3.0
 762   
      * @deprecated To be removed in Tapestry 4.1. Use {@link HiveMind#isNonBlank(java.lang.String)}
 763   
      *             instead.
 764   
      */
 765   
 
 766  0
     public static boolean isNonBlank(String input)
 767   
     {
 768  0
         return HiveMind.isNonBlank(input);
 769   
     }
 770   
 }