Coverage Report - org.apache.tapestry.spec.LibrarySpecification
 
Classes in this File Line Coverage Branch Coverage Complexity
LibrarySpecification
62% 
91% 
1.944
 
 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.spec;
 16  
 
 17  
 import java.util.ArrayList;
 18  
 import java.util.Collections;
 19  
 import java.util.HashMap;
 20  
 import java.util.Iterator;
 21  
 import java.util.List;
 22  
 import java.util.Map;
 23  
 
 24  
 import org.apache.hivemind.ApplicationRuntimeException;
 25  
 import org.apache.hivemind.Location;
 26  
 import org.apache.hivemind.Resource;
 27  
 import org.apache.hivemind.util.ToStringBuilder;
 28  
 import org.apache.tapestry.Tapestry;
 29  
 
 30  
 /**
 31  
  * Specification for a library.
 32  
  * {@link org.apache.tapestry.spec.ApplicationSpecification}is a specialized
 33  
  * kind of library.
 34  
  * 
 35  
  * @author Howard Lewis Ship
 36  
  * @since 2.2bv
 37  
  */
 38  
 
 39  44
 public class LibrarySpecification extends LocatablePropertyHolder implements
 40  
         ILibrarySpecification
 41  
 {
 42  
 
 43  
     /**
 44  
      * Map of page name to page specification path.
 45  
      */
 46  
 
 47  
     private Map _pages;
 48  
 
 49  
     /**
 50  
      * Map of component alias to component specification path.
 51  
      */
 52  
     private Map _components;
 53  
 
 54  
     /**
 55  
      * Map of library id to library specification path.
 56  
      */
 57  
 
 58  
     private Map _libraries;
 59  
 
 60  
     private String _description;
 61  
 
 62  
     /**
 63  
      * Map of extension name to {@link IExtensionSpecification}.
 64  
      */
 65  
 
 66  
     private Map _extensions;
 67  
 
 68  
     /**
 69  
      * Map of extension name to Object for instantiated extensions.
 70  
      */
 71  
 
 72  
     private Map _instantiatedExtensions;
 73  
 
 74  
     /**
 75  
      * The XML Public Id used when the library specification was read (if
 76  
      * applicable).
 77  
      * 
 78  
      * @since 2.2
 79  
      */
 80  
 
 81  
     private String _publicId;
 82  
 
 83  
     /**
 84  
      * The location of the specification.
 85  
      */
 86  
 
 87  
     private Resource _specificationLocation;
 88  
 
 89  
     public String getLibrarySpecificationPath(String id)
 90  
     {
 91  0
         return (String) get(_libraries, id);
 92  
     }
 93  
 
 94  
     /**
 95  
      * Sets the specification path for an embedded library.
 96  
      * 
 97  
      * @throws IllegalArgumentException
 98  
      *             if a library with the given id already exists
 99  
      */
 100  
 
 101  
     public void setLibrarySpecificationPath(String id, String path)
 102  
     {
 103  3
         if (_libraries == null) _libraries = new HashMap();
 104  
 
 105  3
         if (_libraries.containsKey(id))
 106  0
             throw new IllegalArgumentException(Tapestry.format(
 107  
                     "LibrarySpecification.duplicate-child-namespace-id", id));
 108  
 
 109  3
         _libraries.put(id, path);
 110  3
     }
 111  
 
 112  
     public List getLibraryIds()
 113  
     {
 114  1
         return sortedKeys(_libraries);
 115  
     }
 116  
 
 117  
     public String getPageSpecificationPath(String name)
 118  
     {
 119  0
         return (String) get(_pages, name);
 120  
     }
 121  
 
 122  
     public void setPageSpecificationPath(String name, String path)
 123  
     {
 124  18
         if (_pages == null) _pages = new HashMap();
 125  
 
 126  18
         if (_pages.containsKey(name))
 127  0
             throw new IllegalArgumentException(Tapestry.format(
 128  
                     "LibrarySpecification.duplicate-page-name", name));
 129  
 
 130  18
         _pages.put(name, path);
 131  18
     }
 132  
 
 133  
     public List getPageNames()
 134  
     {
 135  1
         return sortedKeys(_pages);
 136  
     }
 137  
 
 138  
     public void setComponentSpecificationPath(String alias, String path)
 139  
     {
 140  9
         if (_components == null) _components = new HashMap();
 141  
 
 142  9
         if (_components.containsKey(alias))
 143  0
             throw new IllegalArgumentException(Tapestry.format(
 144  
                     "LibrarySpecification.duplicate-component-alias", alias));
 145  
 
 146  9
         _components.put(alias, path);
 147  9
     }
 148  
 
 149  
     public String getComponentSpecificationPath(String alias)
 150  
     {
 151  2
         return (String) get(_components, alias);
 152  
     }
 153  
 
 154  
     /**
 155  
      * @since 3.0
 156  
      */
 157  
 
 158  
     public List getComponentTypes()
 159  
     {
 160  2
         return sortedKeys(_components);
 161  
     }
 162  
 
 163  
     private List sortedKeys(Map map)
 164  
     {
 165  4
         if (map == null) return Collections.EMPTY_LIST;
 166  
 
 167  4
         List result = new ArrayList(map.keySet());
 168  
 
 169  4
         Collections.sort(result);
 170  
 
 171  4
         return result;
 172  
     }
 173  
 
 174  
     private Object get(Map map, Object key)
 175  
     {
 176  2
         if (map == null) return null;
 177  
 
 178  2
         return map.get(key);
 179  
     }
 180  
 
 181  
     /**
 182  
      * Returns the documentation for this library..
 183  
      */
 184  
 
 185  
     public String getDescription()
 186  
     {
 187  1
         return _description;
 188  
     }
 189  
 
 190  
     /**
 191  
      * Sets the documentation for this library.
 192  
      */
 193  
 
 194  
     public void setDescription(String description)
 195  
     {
 196  1
         _description = description;
 197  1
     }
 198  
 
 199  
     /**
 200  
      * Returns a Map of extensions; key is extension name, value is
 201  
      * {@link org.apache.tapestry.spec.IExtensionSpecification}. May return
 202  
      * null. The returned Map is immutable.
 203  
      */
 204  
 
 205  
     public Map getExtensionSpecifications()
 206  
     {
 207  0
         if (_extensions == null) return null;
 208  
 
 209  0
         return Collections.unmodifiableMap(_extensions);
 210  
     }
 211  
 
 212  
     /**
 213  
      * Adds another extension specification.
 214  
      * 
 215  
      * @throws IllegalArgumentException
 216  
      *             if an extension with the given name already exists.
 217  
      */
 218  
 
 219  
     public void addExtensionSpecification(String name,
 220  
             IExtensionSpecification extension)
 221  
     {
 222  8
         if (_extensions == null) _extensions = new HashMap();
 223  
 
 224  8
         if (_extensions.containsKey(name))
 225  0
             throw new IllegalArgumentException(Tapestry
 226  
                     .format("LibrarySpecification.duplicate-extension-name",
 227  
                             this, name));
 228  
 
 229  8
         _extensions.put(name, extension);
 230  8
     }
 231  
 
 232  
     /**
 233  
      * Returns a sorted List of the names of all extensions. May return the
 234  
      * empty list, but won't return null.
 235  
      */
 236  
 
 237  
     public synchronized List getExtensionNames()
 238  
     {
 239  0
         return sortedKeys(_instantiatedExtensions);
 240  
     }
 241  
 
 242  
     /**
 243  
      * Returns the named IExtensionSpecification, or null if it doesn't exist.
 244  
      */
 245  
 
 246  
     public IExtensionSpecification getExtensionSpecification(String name)
 247  
     {
 248  9
         if (_extensions == null) return null;
 249  
 
 250  9
         return (IExtensionSpecification) _extensions.get(name);
 251  
     }
 252  
 
 253  
     /**
 254  
      * Returns true if this library specification has a specification for the
 255  
      * named extension.
 256  
      */
 257  
 
 258  
     public boolean checkExtension(String name)
 259  
     {
 260  0
         if (_extensions == null) return false;
 261  
 
 262  0
         return _extensions.containsKey(name);
 263  
     }
 264  
 
 265  
     /**
 266  
      * Returns an instantiated extension. Extensions are created as needed and
 267  
      * cached for later use.
 268  
      * 
 269  
      * @throws IllegalArgumentException
 270  
      *             if no extension specification exists for the given name.
 271  
      */
 272  
 
 273  
     public synchronized Object getExtension(String name)
 274  
     {
 275  2
         return getExtension(name, null);
 276  
     }
 277  
 
 278  
     /** @since 3.0 * */
 279  
 
 280  
     public synchronized Object getExtension(String name, Class typeConstraint)
 281  
     {
 282  5
         if (_instantiatedExtensions == null)
 283  5
             _instantiatedExtensions = new HashMap();
 284  
 
 285  5
         Object result = _instantiatedExtensions.get(name);
 286  5
         IExtensionSpecification spec = getExtensionSpecification(name);
 287  
 
 288  5
         if (spec == null)
 289  0
             throw new IllegalArgumentException(Tapestry.format(
 290  
                     "LibrarySpecification.no-such-extension", name));
 291  
 
 292  5
         if (result == null)
 293  
         {
 294  
 
 295  5
             result = spec.instantiateExtension();
 296  
 
 297  5
             _instantiatedExtensions.put(name, result);
 298  
         }
 299  
 
 300  5
         if (typeConstraint != null)
 301  3
             applyTypeConstraint(name, result, typeConstraint, spec
 302  
                     .getLocation());
 303  
 
 304  4
         return result;
 305  
     }
 306  
 
 307  
     /**
 308  
      * Checks that an extension conforms to the supplied type constraint.
 309  
      * 
 310  
      * @throws IllegalArgumentException
 311  
      *             if the extension fails the check.
 312  
      * @since 3.0
 313  
      */
 314  
 
 315  
     protected void applyTypeConstraint(String name, Object extension,
 316  
             Class typeConstraint, Location location)
 317  
     {
 318  3
         Class extensionClass = extension.getClass();
 319  
 
 320  
         // Can you assign an instance of the extension to a variable
 321  
         // of type typeContraint legally?
 322  
 
 323  3
         if (typeConstraint.isAssignableFrom(extensionClass)) return;
 324  
 
 325  1
         String key = typeConstraint.isInterface() ? "LibrarySpecification.extension-does-not-implement-interface"
 326  
                 : "LibrarySpecification.extension-not-a-subclass";
 327  
 
 328  1
         throw new ApplicationRuntimeException(Tapestry.format(key, name,
 329  
                 extensionClass.getName(), typeConstraint.getName()), location,
 330  
                 null);
 331  
     }
 332  
 
 333  
     /**
 334  
      * Invoked after the entire specification has been constructed to
 335  
      * instantiate any extensions marked immediate.
 336  
      */
 337  
 
 338  
     public synchronized void instantiateImmediateExtensions()
 339  
     {
 340  15
         if (_extensions == null) return;
 341  
 
 342  8
         Iterator i = _extensions.entrySet().iterator();
 343  
 
 344  16
         while(i.hasNext())
 345  
         {
 346  8
             Map.Entry entry = (Map.Entry) i.next();
 347  
 
 348  8
             IExtensionSpecification spec = (IExtensionSpecification) entry
 349  
                     .getValue();
 350  
 
 351  8
             if (!spec.isImmediate()) continue;
 352  
 
 353  1
             String name = (String) entry.getKey();
 354  
 
 355  1
             getExtension(name);
 356  1
         }
 357  
 
 358  8
     }
 359  
 
 360  
     /**
 361  
      * Returns the extensions map.
 362  
      * 
 363  
      * @return Map of objects.
 364  
      */
 365  
 
 366  
     protected Map getExtensions()
 367  
     {
 368  0
         return _extensions;
 369  
     }
 370  
 
 371  
     /**
 372  
      * Updates the extension map.
 373  
      * 
 374  
      * @param extension
 375  
      *            A Map of extension specification paths keyed on extension id.
 376  
      *            <p>
 377  
      *            The map is retained, not copied.
 378  
      */
 379  
 
 380  
     protected void setExtensions(Map extension)
 381  
     {
 382  0
         _extensions = extension;
 383  0
     }
 384  
 
 385  
     /**
 386  
      * Returns the libraries map.
 387  
      * 
 388  
      * @return Map of {@link LibrarySpecification}.
 389  
      */
 390  
 
 391  
     protected Map getLibraries()
 392  
     {
 393  0
         return _libraries;
 394  
     }
 395  
 
 396  
     /**
 397  
      * Updates the library map.
 398  
      * 
 399  
      * @param libraries
 400  
      *            A Map of library specification paths keyed on library id.
 401  
      *            <p>
 402  
      *            The map is retained, not copied.
 403  
      */
 404  
 
 405  
     protected void setLibraries(Map libraries)
 406  
     {
 407  0
         _libraries = libraries;
 408  0
     }
 409  
 
 410  
     /**
 411  
      * Returns the pages map.
 412  
      * 
 413  
      * @return Map of {@link IComponentSpecification}.
 414  
      */
 415  
 
 416  
     protected Map getPages()
 417  
     {
 418  0
         return _pages;
 419  
     }
 420  
 
 421  
     /**
 422  
      * Updates the page map.
 423  
      * 
 424  
      * @param pages
 425  
      *            A Map of page specification paths keyed on page id.
 426  
      *            <p>
 427  
      *            The map is retained, not copied.
 428  
      */
 429  
 
 430  
     protected void setPages(Map pages)
 431  
     {
 432  0
         _pages = pages;
 433  0
     }
 434  
 
 435  
     /**
 436  
      * Returns the components map.
 437  
      * 
 438  
      * @return Map of {@link IContainedComponent}.
 439  
      */
 440  
 
 441  
     protected Map getComponents()
 442  
     {
 443  0
         return _components;
 444  
     }
 445  
 
 446  
     /**
 447  
      * Updates the components map.
 448  
      * 
 449  
      * @param components
 450  
      *            A Map of {@link IContainedComponent}keyed on component id.
 451  
      *            The map is retained, not copied.
 452  
      */
 453  
 
 454  
     protected void setComponents(Map components)
 455  
     {
 456  0
         _components = components;
 457  0
     }
 458  
 
 459  
     /**
 460  
      * Returns the XML Public Id for the library file, or null if not
 461  
      * applicable.
 462  
      * <p>
 463  
      * This method exists as a convienience for the Spindle plugin. A previous
 464  
      * method used an arbitrary version string, the public id is more useful and
 465  
      * less ambiguous.
 466  
      */
 467  
 
 468  
     public String getPublicId()
 469  
     {
 470  0
         return _publicId;
 471  
     }
 472  
 
 473  
     public void setPublicId(String publicId)
 474  
     {
 475  0
         _publicId = publicId;
 476  0
     }
 477  
 
 478  
     /** @since 3.0 * */
 479  
 
 480  
     public Resource getSpecificationLocation()
 481  
     {
 482  116
         return _specificationLocation;
 483  
     }
 484  
 
 485  
     /** @since 3.0 * */
 486  
 
 487  
     public void setSpecificationLocation(Resource specificationLocation)
 488  
     {
 489  35
         _specificationLocation = specificationLocation;
 490  35
     }
 491  
 
 492  
     /** @since 3.0 * */
 493  
 
 494  
     public synchronized String toString()
 495  
     {
 496  0
         ToStringBuilder builder = new ToStringBuilder(this);
 497  
 
 498  0
         builder.append("components", _components);
 499  0
         builder.append("description", _description);
 500  0
         builder.append("instantiatedExtensions", _instantiatedExtensions);
 501  0
         builder.append("libraries", _libraries);
 502  0
         builder.append("pages", _pages);
 503  0
         builder.append("publicId", _publicId);
 504  0
         builder.append("specificationLocation", _specificationLocation);
 505  
 
 506  0
         extendDescription(builder);
 507  
 
 508  0
         return builder.toString();
 509  
     }
 510  
 
 511  
     /**
 512  
      * Does nothing, subclasses may override to add additional description.
 513  
      * 
 514  
      * @see #toString()
 515  
      * @since 3.0
 516  
      */
 517  
 
 518  
     protected void extendDescription(ToStringBuilder builder)
 519  
     {
 520  0
     }
 521  
 
 522  
 }