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: 433   Methods: 15
NCLOC: 265   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
ComponentTemplateLoaderLogic.java 83.9% 89.9% 93.3% 88.3%
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.services.impl;
 16   
 
 17   
 import java.util.HashSet;
 18   
 import java.util.Iterator;
 19   
 import java.util.Map;
 20   
 import java.util.Set;
 21   
 
 22   
 import org.apache.commons.logging.Log;
 23   
 import org.apache.hivemind.ApplicationRuntimeException;
 24   
 import org.apache.hivemind.Location;
 25   
 import org.apache.tapestry.BaseComponent;
 26   
 import org.apache.tapestry.IBinding;
 27   
 import org.apache.tapestry.IComponent;
 28   
 import org.apache.tapestry.IRender;
 29   
 import org.apache.tapestry.IRequestCycle;
 30   
 import org.apache.tapestry.ITemplateComponent;
 31   
 import org.apache.tapestry.Tapestry;
 32   
 import org.apache.tapestry.binding.BindingConstants;
 33   
 import org.apache.tapestry.binding.BindingSource;
 34   
 import org.apache.tapestry.binding.BindingUtils;
 35   
 import org.apache.tapestry.binding.LiteralBinding;
 36   
 import org.apache.tapestry.engine.IPageLoader;
 37   
 import org.apache.tapestry.parse.CloseToken;
 38   
 import org.apache.tapestry.parse.ComponentTemplate;
 39   
 import org.apache.tapestry.parse.LocalizationToken;
 40   
 import org.apache.tapestry.parse.OpenToken;
 41   
 import org.apache.tapestry.parse.TemplateToken;
 42   
 import org.apache.tapestry.parse.TextToken;
 43   
 import org.apache.tapestry.parse.TokenType;
 44   
 import org.apache.tapestry.services.TemplateSource;
 45   
 import org.apache.tapestry.spec.IComponentSpecification;
 46   
 import org.apache.tapestry.spec.IContainedComponent;
 47   
 
 48   
 /**
 49   
  * Contains the logic from {@link org.apache.tapestry.services.impl.ComponentTemplateLoaderImpl},
 50   
  * which creates one of these instances to process the request. This is necessary because the
 51   
  * service must be re-entrant (because templates can contain components that have templates).
 52   
  * 
 53   
  * @author Howard Lewis Ship
 54   
  * @since 4.0
 55   
  */
 56   
 public class ComponentTemplateLoaderLogic
 57   
 {
 58   
     private Log _log;
 59   
 
 60   
     private IPageLoader _pageLoader;
 61   
 
 62   
     private IRequestCycle _requestCycle;
 63   
 
 64   
     private ITemplateComponent _loadComponent;
 65   
 
 66   
     private BindingSource _bindingSource;
 67   
 
 68   
     private IComponent[] _stack;
 69   
 
 70   
     private int _stackx;
 71   
 
 72   
     private IComponent _activeComponent = null;
 73   
 
 74   
     private Set _seenIds = new HashSet();
 75   
 
 76  227
     public ComponentTemplateLoaderLogic(Log log, IPageLoader pageLoader, BindingSource bindingSource)
 77   
     {
 78  227
         _log = log;
 79  227
         _pageLoader = pageLoader;
 80  227
         _bindingSource = bindingSource;
 81   
     }
 82   
 
 83  227
     public void loadTemplate(IRequestCycle requestCycle, ITemplateComponent loadComponent,
 84   
             ComponentTemplate template)
 85   
     {
 86  227
         _requestCycle = requestCycle;
 87  227
         _loadComponent = loadComponent;
 88   
 
 89  227
         process(template);
 90   
     }
 91   
 
 92  227
     private void process(ComponentTemplate template)
 93   
     {
 94  227
         int count = template.getTokenCount();
 95   
 
 96  227
         _stack = new IComponent[count];
 97   
 
 98  227
         for (int i = 0; i < count; i++)
 99   
         {
 100  3668
             TemplateToken token = template.getToken(i);
 101   
 
 102  3668
             TokenType type = token.getType();
 103   
 
 104  3668
             if (type == TokenType.TEXT)
 105   
             {
 106  1620
                 process((TextToken) token);
 107  1620
                 continue;
 108   
             }
 109   
 
 110  2048
             if (type == TokenType.OPEN)
 111   
             {
 112  1023
                 process((OpenToken) token);
 113  1016
                 continue;
 114   
             }
 115   
 
 116  1025
             if (type == TokenType.CLOSE)
 117   
             {
 118  1011
                 process((CloseToken) token);
 119  1011
                 continue;
 120   
             }
 121   
 
 122  14
             if (type == TokenType.LOCALIZATION)
 123   
             {
 124  14
                 process((LocalizationToken) token);
 125  14
                 continue;
 126   
             }
 127   
         }
 128   
 
 129   
         // This is also pretty much unreachable, and the message is kind of out
 130   
         // of date, too.
 131   
 
 132  220
         if (_stackx != 0)
 133  0
             throw new ApplicationRuntimeException(Tapestry
 134   
                     .getMessage("BaseComponent.unbalance-open-tags"), _loadComponent, null, null);
 135   
 
 136  220
         checkAllComponentsReferenced();
 137   
     }
 138   
 
 139   
     /**
 140   
      * Adds the token (which implements {@link IRender}) to the active component (using
 141   
      * {@link IComponent#addBody(IRender)}), or to this component
 142   
      * {@link BaseComponent#addOuter(IRender)}.
 143   
      * <p>
 144   
      * A check is made that the active component allows a body.
 145   
      */
 146   
 
 147  1620
     private void process(TextToken token)
 148   
     {
 149  1620
         if (_activeComponent == null)
 150   
         {
 151  441
             _loadComponent.addOuter(token);
 152  441
             return;
 153   
         }
 154   
 
 155  1179
         if (!_activeComponent.getSpecification().getAllowBody())
 156  0
             throw createBodylessComponentException(_activeComponent);
 157   
 
 158  1179
         _activeComponent.addBody(token);
 159   
     }
 160   
 
 161  1023
     private void process(OpenToken token)
 162   
     {
 163  1023
         String id = token.getId();
 164  1023
         IComponent component = null;
 165  1023
         String componentType = token.getComponentType();
 166   
 
 167  1023
         if (componentType == null)
 168  413
             component = getEmbeddedComponent(id);
 169   
         else
 170   
         {
 171  610
             checkForDuplicateId(id, token.getLocation());
 172   
 
 173  609
             component = createImplicitComponent(id, componentType, token.getLocation());
 174   
         }
 175   
 
 176   
         // Make sure the template contains each component only once.
 177   
 
 178  1019
         if (_seenIds.contains(id))
 179  0
             throw new ApplicationRuntimeException(ImplMessages.multipleComponentReferences(
 180   
                     _loadComponent,
 181   
                     id), _loadComponent, token.getLocation(), null);
 182   
 
 183  1019
         _seenIds.add(id);
 184   
 
 185  1019
         if (_activeComponent == null)
 186  338
             _loadComponent.addOuter(component);
 187   
         else
 188   
         {
 189   
             // Note: this code may no longer be reachable (because the
 190   
             // template parser does this check first).
 191   
 
 192  681
             if (!_activeComponent.getSpecification().getAllowBody())
 193  0
                 throw createBodylessComponentException(_activeComponent);
 194   
 
 195  681
             _activeComponent.addBody(component);
 196   
         }
 197   
 
 198  1019
         addTemplateBindings(component, token);
 199   
 
 200  1016
         _stack[_stackx++] = _activeComponent;
 201   
 
 202  1016
         _activeComponent = component;
 203   
     }
 204   
 
 205  610
     private void checkForDuplicateId(String id, Location location)
 206   
     {
 207  610
         if (id == null)
 208  0
             return;
 209   
 
 210  610
         IContainedComponent cc = _loadComponent.getSpecification().getComponent(id);
 211   
 
 212  610
         if (cc != null)
 213  1
             throw new ApplicationRuntimeException(ImplMessages.dupeComponentId(id, cc),
 214   
                     _loadComponent, location, null);
 215   
     }
 216   
 
 217  609
     private IComponent createImplicitComponent(String id, String componentType, Location location)
 218   
     {
 219  609
         IComponent result = _pageLoader.createImplicitComponent(
 220   
                 _requestCycle,
 221   
                 _loadComponent,
 222   
                 id,
 223   
                 componentType,
 224   
                 location);
 225   
 
 226  606
         return result;
 227   
     }
 228   
 
 229  413
     private IComponent getEmbeddedComponent(String id)
 230   
     {
 231  413
         return _loadComponent.getComponent(id);
 232   
     }
 233   
 
 234  1011
     private void process(CloseToken token)
 235   
     {
 236   
         // Again, this is pretty much impossible to reach because
 237   
         // the template parser does a great job.
 238   
 
 239  1011
         if (_stackx <= 0)
 240  0
             throw new ApplicationRuntimeException(ImplMessages.unbalancedCloseTags(),
 241   
                     _loadComponent, token.getLocation(), null);
 242   
 
 243   
         // Null and forget the top element on the stack.
 244   
 
 245  1011
         _stack[_stackx--] = null;
 246   
 
 247  1011
         _activeComponent = _stack[_stackx];
 248   
     }
 249   
 
 250  14
     private void process(LocalizationToken token)
 251   
     {
 252  14
         IRender render = new LocalizedStringRender(_loadComponent, token);
 253   
 
 254  14
         if (_activeComponent == null)
 255  7
             _loadComponent.addOuter(render);
 256   
         else
 257  7
             _activeComponent.addBody(render);
 258   
     }
 259   
 
 260   
     /**
 261   
      * Adds bindings based on attributes in the template.
 262   
      */
 263   
 
 264  1019
     private void addTemplateBindings(IComponent component, OpenToken token)
 265   
     {
 266  1019
         IComponentSpecification spec = component.getSpecification();
 267   
 
 268  1019
         Map attributes = token.getAttributesMap();
 269   
 
 270  1019
         if (attributes != null)
 271   
         {
 272  539
             Iterator i = attributes.entrySet().iterator();
 273   
 
 274  539
             while (i.hasNext())
 275   
             {
 276  711
                 Map.Entry entry = (Map.Entry) i.next();
 277   
 
 278  711
                 String name = (String) entry.getKey();
 279  711
                 String value = (String) entry.getValue();
 280   
 
 281  711
                 String description = ImplMessages.templateParameterName(name);
 282   
 
 283   
                 // For informal parameters, or formal parameters that don't define a default binding
 284   
                 // type,
 285   
                 // treat the value as a literal.
 286   
 
 287  711
                 String defaultBindingType = BindingUtils.getDefaultBindingType(
 288   
                         spec,
 289   
                         name,
 290   
                         BindingConstants.LITERAL_PREFIX);
 291   
 
 292  711
                 IBinding binding = _bindingSource.createBinding(
 293   
                         _loadComponent,
 294   
                         description,
 295   
                         value,
 296   
                         defaultBindingType,
 297   
                         token.getLocation());
 298   
 
 299  711
                 addBinding(component, spec, name, binding);
 300   
             }
 301   
         }
 302   
 
 303   
         // if the component defines a templateTag parameter and
 304   
         // there is no established binding for that parameter,
 305   
         // add a static binding carrying the template tag
 306   
 
 307  1016
         if (spec.getParameter(TemplateSource.TEMPLATE_TAG_PARAMETER_NAME) != null
 308   
                 && component.getBinding(TemplateSource.TEMPLATE_TAG_PARAMETER_NAME) == null)
 309   
         {
 310  6
             IBinding binding = _bindingSource.createBinding(
 311   
                     component,
 312   
                     TemplateSource.TEMPLATE_TAG_PARAMETER_NAME,
 313   
                     token.getTag(),
 314   
                     BindingConstants.LITERAL_PREFIX,
 315   
                     token.getLocation());
 316   
 
 317  6
             addBinding(component, spec, TemplateSource.TEMPLATE_TAG_PARAMETER_NAME, binding);
 318   
         }
 319   
     }
 320   
 
 321   
     /**
 322   
      * Adds an expression binding, checking for errors related to reserved and informal parameters.
 323   
      * <p>
 324   
      * It is an error to specify expression bindings in both the specification and the template.
 325   
      */
 326   
 
 327  717
     private void addBinding(IComponent component, IComponentSpecification spec, String name,
 328   
             IBinding binding)
 329   
     {
 330   
 
 331   
         // If matches a formal parameter name, allow it to be set
 332   
         // unless there's already a binding.
 333   
 
 334  717
         boolean valid = validate(component, spec, name, binding);
 335   
 
 336  714
         if (valid)
 337  684
             component.setBinding(name, binding);
 338   
     }
 339   
 
 340  717
     private boolean validate(IComponent component, IComponentSpecification spec, String name,
 341   
             IBinding binding)
 342   
     {
 343   
         // TODO: This is ugly! Need a better/smarter way, even if we have to extend BindingSource
 344   
         // to tell us.
 345   
 
 346  717
         boolean literal = binding instanceof LiteralBinding;
 347   
 
 348  717
         boolean isFormal = (spec.getParameter(name) != null);
 349   
 
 350  717
         if (isFormal)
 351   
         {
 352  650
             if (component.getBinding(name) != null)
 353   
             {
 354   
                 // Literal bindings in the template that conflict with bound parameters
 355   
                 // from the spec are silently ignored.
 356   
 
 357  2
                 if (literal)
 358  1
                     return false;
 359   
 
 360  1
                 throw new ApplicationRuntimeException(ImplMessages.dupeTemplateBinding(
 361   
                         name,
 362   
                         component,
 363   
                         _loadComponent), component, binding.getLocation(), null);
 364   
             }
 365   
 
 366  648
             return true;
 367   
         }
 368   
 
 369  67
         if (!spec.getAllowInformalParameters())
 370   
         {
 371   
             // Again; if informal parameters are disallowed, ignore literal bindings, as they
 372   
             // are there as placeholders or for WYSIWYG.
 373   
 
 374  1
             if (literal)
 375  0
                 return false;
 376   
 
 377  1
             throw new ApplicationRuntimeException(ImplMessages.templateBindingForInformalParameter(
 378   
                     _loadComponent,
 379   
                     name,
 380   
                     component), component, binding.getLocation(), null);
 381   
         }
 382   
 
 383   
         // If the name is reserved (matches a formal parameter
 384   
         // or reserved name, caselessly), then skip it.
 385   
 
 386  66
         if (spec.isReservedParameterName(name))
 387   
         {
 388   
             // Final case for literals: if they conflict with a reserved name, they are ignored.
 389   
             // Again, there for WYSIWYG.
 390   
 
 391  30
             if (literal)
 392  29
                 return false;
 393   
 
 394  1
             throw new ApplicationRuntimeException(ImplMessages.templateBindingForReservedParameter(
 395   
                     _loadComponent,
 396   
                     name,
 397   
                     component), component, binding.getLocation(), null);
 398   
         }
 399   
 
 400  36
         return true;
 401   
     }
 402   
 
 403  220
     private void checkAllComponentsReferenced()
 404   
     {
 405   
         // First, contruct a modifiable copy of the ids of all expected components
 406   
         // (that is, components declared in the specification).
 407   
 
 408  220
         Map components = _loadComponent.getComponents();
 409   
 
 410  220
         Set ids = components.keySet();
 411   
 
 412   
         // If the seen ids ... ids referenced in the template, matches
 413   
         // all the ids in the specification then we're fine.
 414   
 
 415  220
         if (_seenIds.containsAll(ids))
 416  220
             return;
 417   
 
 418   
         // Create a modifiable copy. Remove the ids that are referenced in
 419   
         // the template. The remainder are worthy of note.
 420   
 
 421  0
         ids = new HashSet(ids);
 422  0
         ids.removeAll(_seenIds);
 423   
 
 424  0
         _log.error(ImplMessages.missingComponentSpec(_loadComponent, ids));
 425   
 
 426   
     }
 427   
 
 428  0
     private ApplicationRuntimeException createBodylessComponentException(IComponent component)
 429   
     {
 430  0
         return new ApplicationRuntimeException(ImplMessages.bodylessComponent(), component, null,
 431   
                 null);
 432   
     }
 433   
 }