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