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