Clover coverage report - Code Coverage for tapestry release 4.0-alpha-3
Coverage timestamp: Mon May 16 2005 09:05:49 EDT
file stats: LOC: 769   Methods: 27
NCLOC: 288   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 60.5% 54.8% 63% 57.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.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 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 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 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 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 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 RESTART_SERVICE = "restart";
 110   
 
 111   
     /**
 112   
      * The name ("asset") of a service used to access internal assets.
 113   
      */
 114   
 
 115   
     public static final String 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 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 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 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 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 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 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 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 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 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 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 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 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 OGNL_TYPE_CONVERTER = "org.apache.tapestry.ognl-type-converter";
 244   
 
 245   
     /**
 246   
      * Prevent instantiation.
 247   
      */
 248   
 
 249  0
     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 VERSION = readVersion();
 258   
 
 259   
     /**
 260   
      * Contains strings loaded from TapestryStrings.properties.
 261   
      * 
 262   
      * @since 1.0.8
 263   
      */
 264   
 
 265   
     private static ResourceBundle _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 _localeMap = new HashMap();
 273   
 
 274   
     static
 275   
     {
 276  1
         Locale[] locales = Locale.getAvailableLocales();
 277  1
         for (int i = 0; i < locales.length; i++)
 278   
         {
 279  134
             _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 _invokedMethodIds = new ThreadLocal();
 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  0
     public static void copyInformalBindings(IComponent source, IComponent destination)
 296   
     {
 297  0
         Collection names = source.getBindingNames();
 298   
 
 299  0
         if (names == null)
 300  0
             return;
 301   
 
 302  0
         IComponentSpecification specification = source.getSpecification();
 303  0
         Iterator i = names.iterator();
 304   
 
 305  0
         while (i.hasNext())
 306   
         {
 307  0
             String name = (String) i.next();
 308   
 
 309   
             // If not a formal parameter, then copy it over.
 310   
 
 311  0
             if (specification.getParameter(name) == null)
 312   
             {
 313  0
                 IBinding binding = source.getBinding(name);
 314   
 
 315  0
                 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  0
     public static Locale getLocale(String s)
 327   
     {
 328  0
         Locale result = null;
 329   
 
 330  0
         synchronized (_localeMap)
 331   
         {
 332  0
             result = (Locale) _localeMap.get(s);
 333   
         }
 334   
 
 335  0
         if (result == null)
 336   
         {
 337  0
             StringSplitter splitter = new StringSplitter('_');
 338  0
             String[] terms = splitter.splitToArray(s);
 339   
 
 340  0
             switch (terms.length)
 341   
             {
 342   
                 case 1:
 343   
 
 344  0
                     result = new Locale(terms[0], "");
 345  0
                     break;
 346   
 
 347   
                 case 2:
 348   
 
 349  0
                     result = new Locale(terms[0], terms[1]);
 350  0
                     break;
 351   
 
 352   
                 case 3:
 353   
 
 354  0
                     result = new Locale(terms[0], terms[1], terms[2]);
 355  0
                     break;
 356   
 
 357   
                 default:
 358   
 
 359  0
                     throw new IllegalArgumentException("Unable to convert '" + s + "' to a Locale.");
 360   
             }
 361   
 
 362  0
             synchronized (_localeMap)
 363   
             {
 364  0
                 _localeMap.put(s, result);
 365   
             }
 366   
 
 367   
         }
 368   
 
 369  0
         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  175
     public static void close(InputStream stream)
 380   
     {
 381  175
         if (stream != null)
 382   
         {
 383  175
             try
 384   
             {
 385  175
                 stream.close();
 386   
             }
 387   
             catch (IOException 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  29
     public static String format(String key, Object[] args)
 402   
     {
 403  29
         if (_strings == null)
 404  1
             _strings = ResourceBundle.getBundle("org.apache.tapestry.TapestryStrings");
 405   
 
 406  29
         String pattern = _strings.getString(key);
 407   
 
 408  29
         if (args == null)
 409  13
             return pattern;
 410   
 
 411  16
         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  13
     public static String getMessage(String key)
 421   
     {
 422  13
         return format(key, null);
 423   
     }
 424   
 
 425   
     /**
 426   
      * Convienience method for invoking {@link #format(String, Object[])}.
 427   
      * 
 428   
      * @since 3.0
 429   
      */
 430   
 
 431  4
     public static String format(String key, Object arg)
 432   
     {
 433  4
         return format(key, new Object[]
 434   
         { arg });
 435   
     }
 436   
 
 437   
     /**
 438   
      * Convienience method for invoking {@link #format(String, Object[])}.
 439   
      * 
 440   
      * @since 3.0
 441   
      */
 442   
 
 443  9
     public static String format(String key, Object arg1, Object arg2)
 444   
     {
 445  9
         return format(key, new Object[]
 446   
         { arg1, arg2 });
 447   
     }
 448   
 
 449   
     /**
 450   
      * Convienience method for invoking {@link #format(String, Object[])}.
 451   
      * 
 452   
      * @since 3.0
 453   
      */
 454   
 
 455  3
     public static String format(String key, Object arg1, Object arg2, Object arg3)
 456   
     {
 457  3
         return format(key, new Object[]
 458   
         { arg1, arg2, arg3 });
 459   
     }
 460   
 
 461   
     private static final String UNKNOWN_VERSION = "Unknown";
 462   
 
 463   
     /**
 464   
      * Invoked when the class is initialized to read the current version file.
 465   
      */
 466   
 
 467  1
     private static final String readVersion()
 468   
     {
 469  1
         Properties props = new Properties();
 470   
 
 471  1
         try
 472   
         {
 473  1
             InputStream in = Tapestry.class.getResourceAsStream("version.properties");
 474   
 
 475  1
             if (in == null)
 476  0
                 return UNKNOWN_VERSION;
 477   
 
 478  1
             props.load(in);
 479   
 
 480  1
             in.close();
 481   
 
 482  1
             return props.getProperty("project.version", UNKNOWN_VERSION);
 483   
         }
 484   
         catch (IOException ex)
 485   
         {
 486  0
             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  2318
     public static int size(Collection c)
 498   
     {
 499  2318
         if (c == null)
 500  1
             return 0;
 501   
 
 502  2317
         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  339
     public static int size(Object[] array)
 512   
     {
 513  339
         if (array == null)
 514  58
             return 0;
 515   
 
 516  281
         return array.length;
 517   
     }
 518   
 
 519   
     /**
 520   
      * Returns true if the Map is null or empty.
 521   
      * 
 522   
      * @since 3.0
 523   
      */
 524   
 
 525  0
     public static boolean isEmpty(Map map)
 526   
     {
 527  0
         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  90
     public static boolean isEmpty(Collection c)
 537   
     {
 538  90
         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  0
     public static Object[] convertMapToArray(Map map)
 554   
     {
 555  0
         if (isEmpty(map))
 556  0
             return null;
 557   
 
 558  0
         Set entries = map.entrySet();
 559   
 
 560  0
         Object[] result = new Object[2 * entries.size()];
 561  0
         int x = 0;
 562   
 
 563  0
         Iterator i = entries.iterator();
 564  0
         while (i.hasNext())
 565   
         {
 566  0
             Map.Entry entry = (Map.Entry) i.next();
 567   
 
 568  0
             result[x++] = entry.getKey();
 569  0
             result[x++] = entry.getValue();
 570   
         }
 571   
 
 572  0
         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  39
     public static Map convertArrayToMap(Object[] array)
 584   
     {
 585  39
         if (array == null || array.length == 0)
 586  4
             return null;
 587   
 
 588  35
         if (array.length % 2 != 0)
 589  0
             throw new IllegalArgumentException(getMessage("Tapestry.even-sized-array"));
 590   
 
 591  35
         Map result = new HashMap();
 592   
 
 593  35
         int x = 0;
 594  35
         while (x < array.length)
 595   
         {
 596  64
             Object key = array[x++];
 597  64
             Object value = array[x++];
 598   
 
 599  64
             result.put(key, value);
 600   
         }
 601   
 
 602  35
         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  6
     public static String getClassName(Class subject)
 614   
     {
 615  6
         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  0
     public static BindingException createNullBindingException(IBinding binding)
 625   
     {
 626  0
         return new BindingException(getMessage("null-value-for-binding"), binding);
 627   
     }
 628   
 
 629   
     /** @since 3.0 * */
 630   
 
 631  0
     public static ApplicationRuntimeException createNoSuchComponentException(IComponent component,
 632   
             String id, Location location)
 633   
     {
 634  0
         return new ApplicationRuntimeException(format("no-such-component", component
 635   
                 .getExtendedId(), id), component, location, null);
 636   
     }
 637   
 
 638   
     /** @since 3.0 * */
 639   
 
 640  1
     public static BindingException createRequiredParameterException(IComponent component,
 641   
             String parameterName)
 642   
     {
 643  1
         return new BindingException(format("required-parameter", parameterName, component
 644   
                 .getExtendedId()), component, null, component.getBinding(parameterName), null);
 645   
     }
 646   
 
 647   
     /** @since 3.0 * */
 648   
 
 649  0
     public static ApplicationRuntimeException createRenderOnlyPropertyException(
 650   
             IComponent component, String propertyName)
 651   
     {
 652  0
         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  424
     public static void clearMethodInvocations()
 666   
     {
 667  424
         _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  430
     public static void addMethodInvocation(Object methodId)
 679   
     {
 680  430
         List methodIds = (List) _invokedMethodIds.get();
 681   
 
 682  430
         if (methodIds == null)
 683   
         {
 684  424
             methodIds = new ArrayList();
 685  424
             _invokedMethodIds.set(methodIds);
 686   
         }
 687   
 
 688  430
         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  420
     public static void checkMethodInvocation(Object methodId, String methodName, Object object)
 706   
     {
 707  420
         List methodIds = (List) _invokedMethodIds.get();
 708   
 
 709  420
         if (methodIds != null && methodIds.contains(methodId))
 710  419
             return;
 711   
 
 712  1
         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  52
     public static void fireObservedChange(IComponent component, String propertyName, Object newValue)
 730   
     {
 731  52
         ChangeObserver observer = component.getPage().getChangeObserver();
 732   
 
 733  52
         if (observer == null)
 734  22
             return;
 735   
 
 736  30
         ObservedChangeEvent event = new ObservedChangeEvent(component, propertyName, newValue);
 737   
 
 738  30
         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  0
     public static boolean isBlank(String input)
 753   
     {
 754  0
         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  0
     public static boolean isNonBlank(String input)
 766   
     {
 767  0
         return HiveMind.isNonBlank(input);
 768   
     }
 769   
 }