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