Coverage Report - org.apache.tiles.impl.BasicTilesContainer
 
Classes in this File Line Coverage Branch Coverage Complexity
BasicTilesContainer
100%
98/98
96%
25/26
1.88
 
 1  
 /*
 2  
  * $Id: BasicTilesContainer.java 1330672 2012-04-26 06:58:16Z nlebas $
 3  
  *
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  * http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 package org.apache.tiles.impl;
 22  
 
 23  
 import java.io.IOException;
 24  
 import java.util.Deque;
 25  
 import java.util.LinkedList;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.apache.tiles.Attribute;
 29  
 import org.apache.tiles.AttributeContext;
 30  
 import org.apache.tiles.BasicAttributeContext;
 31  
 import org.apache.tiles.Definition;
 32  
 import org.apache.tiles.TilesContainer;
 33  
 import org.apache.tiles.definition.DefinitionsFactory;
 34  
 import org.apache.tiles.definition.NoSuchDefinitionException;
 35  
 import org.apache.tiles.evaluator.AttributeEvaluator;
 36  
 import org.apache.tiles.evaluator.AttributeEvaluatorFactory;
 37  
 import org.apache.tiles.evaluator.AttributeEvaluatorFactoryAware;
 38  
 import org.apache.tiles.preparer.ViewPreparer;
 39  
 import org.apache.tiles.preparer.factory.NoSuchPreparerException;
 40  
 import org.apache.tiles.preparer.factory.PreparerFactory;
 41  
 import org.apache.tiles.request.ApplicationContext;
 42  
 import org.apache.tiles.request.Request;
 43  
 import org.apache.tiles.request.render.CannotRenderException;
 44  
 import org.apache.tiles.request.render.Renderer;
 45  
 import org.apache.tiles.request.render.RendererFactory;
 46  
 import org.slf4j.Logger;
 47  
 import org.slf4j.LoggerFactory;
 48  
 
 49  
 /**
 50  
  * Basic implementation of the tiles container interface.
 51  
  * In most cases, this container will be customized by
 52  
  * injecting customized services, not necessarily by
 53  
  * override the container
 54  
  *
 55  
  * @since 2.0
 56  
  * @version $Rev: 1330672 $ $Date: 2012-04-26 16:58:16 +1000 (Thu, 26 Apr 2012) $
 57  
  */
 58  38
 public class BasicTilesContainer implements TilesContainer,
 59  
         AttributeEvaluatorFactoryAware {
 60  
 
 61  
     /**
 62  
      * Name used to store attribute context stack.
 63  
      */
 64  
     private static final String ATTRIBUTE_CONTEXT_STACK =
 65  
         "org.apache.tiles.AttributeContext.STACK";
 66  
 
 67  
     /**
 68  
      * Log instance for all BasicTilesContainer
 69  
      * instances.
 70  
      */
 71  38
     private final Logger log = LoggerFactory
 72  
             .getLogger(BasicTilesContainer.class);
 73  
 
 74  
     /**
 75  
      * The Tiles application context object.
 76  
      */
 77  
     private ApplicationContext context;
 78  
 
 79  
     /**
 80  
      * The definitions factory.
 81  
      */
 82  
     private DefinitionsFactory definitionsFactory;
 83  
 
 84  
     /**
 85  
      * The preparer factory.
 86  
      */
 87  
     private PreparerFactory preparerFactory;
 88  
 
 89  
     /**
 90  
      * The renderer factory.
 91  
      */
 92  
     private RendererFactory rendererFactory;
 93  
 
 94  
     /**
 95  
      * The attribute evaluator.
 96  
      */
 97  
     private AttributeEvaluatorFactory attributeEvaluatorFactory;
 98  
 
 99  
     /** {@inheritDoc} */
 100  
     public AttributeContext startContext(Request request) {
 101  1
         AttributeContext context = new BasicAttributeContext();
 102  1
         Deque<AttributeContext>  stack = getContextStack(request);
 103  1
         if (!stack.isEmpty()) {
 104  1
             AttributeContext parent = stack.peek();
 105  1
             context.inheritCascadedAttributes(parent);
 106  
         }
 107  1
         stack.push(context);
 108  1
         return context;
 109  
     }
 110  
 
 111  
     /** {@inheritDoc} */
 112  
     public void endContext(Request request) {
 113  1
         popContext(request);
 114  1
     }
 115  
 
 116  
     /** {@inheritDoc} */
 117  
     public void renderContext(Request request) {
 118  1
         AttributeContext attributeContext = getAttributeContext(request);
 119  
 
 120  1
         render(request, attributeContext);
 121  1
     }
 122  
 
 123  
     /**
 124  
      * Returns the Tiles application context used by this container.
 125  
      *
 126  
      * @return the application context for this container.
 127  
      */
 128  
     public ApplicationContext getApplicationContext() {
 129  1
         return context;
 130  
     }
 131  
 
 132  
     /**
 133  
      * Sets the Tiles application context to use.
 134  
      *
 135  
      * @param context The Tiles application context.
 136  
      */
 137  
     public void setApplicationContext(ApplicationContext context) {
 138  38
         this.context = context;
 139  38
     }
 140  
 
 141  
     /** {@inheritDoc} */
 142  
     public AttributeContext getAttributeContext(Request request) {
 143  6
         AttributeContext context = getContext(request);
 144  6
         if (context == null) {
 145  1
             context = new BasicAttributeContext();
 146  1
             pushContext(context, request);
 147  
         }
 148  6
         return context;
 149  
 
 150  
     }
 151  
 
 152  
     /**
 153  
      * Returns the definitions factory.
 154  
      *
 155  
      * @return The definitions factory used by this container.
 156  
      */
 157  
     public DefinitionsFactory getDefinitionsFactory() {
 158  2
         return definitionsFactory;
 159  
     }
 160  
 
 161  
     /**
 162  
      * Set the definitions factory. This method first ensures
 163  
      * that the container has not yet been initialized.
 164  
      *
 165  
      * @param definitionsFactory the definitions factory for this instance.
 166  
      */
 167  
     public void setDefinitionsFactory(DefinitionsFactory definitionsFactory) {
 168  38
         this.definitionsFactory = definitionsFactory;
 169  38
     }
 170  
 
 171  
     /**
 172  
      * Returns the preparer factory used by this container.
 173  
      *
 174  
      * @return return the preparerInstance factory used by this container.
 175  
      */
 176  
     public PreparerFactory getPreparerFactory() {
 177  2
         return preparerFactory;
 178  
     }
 179  
 
 180  
     /**
 181  
      * Set the preparerInstance factory.  This method first ensures
 182  
      * that the container has not yet been initialized.
 183  
      *
 184  
      * @param preparerFactory the preparerInstance factory for this conainer.
 185  
      */
 186  
     public void setPreparerFactory(PreparerFactory preparerFactory) {
 187  38
         this.preparerFactory = preparerFactory;
 188  38
     }
 189  
 
 190  
     /**
 191  
      * Sets the renderer instance factory.
 192  
      *
 193  
      * @param rendererFactory the renderer instance factory for this container.
 194  
      * @since 2.1.0
 195  
      */
 196  
     public void setRendererFactory(RendererFactory rendererFactory) {
 197  38
         this.rendererFactory = rendererFactory;
 198  38
     }
 199  
 
 200  
     /** {@inheritDoc} */
 201  
     public void setAttributeEvaluatorFactory(
 202  
             AttributeEvaluatorFactory attributeEvaluatorFactory) {
 203  38
         this.attributeEvaluatorFactory = attributeEvaluatorFactory;
 204  38
     }
 205  
 
 206  
     /** {@inheritDoc} */
 207  
     public void prepare(String preparer, Request request) {
 208  2
         prepare(request, preparer, false);
 209  1
     }
 210  
 
 211  
     /** {@inheritDoc} */
 212  
     public void render(String definitionName, Request request) {
 213  2
         log.debug("Render request received for definition '{}'", definitionName);
 214  
 
 215  2
         Definition definition = getDefinition(definitionName, request);
 216  
 
 217  2
         if (definition == null) {
 218  1
             throw new NoSuchDefinitionException("Unable to find the definition '" + definitionName + "'");
 219  
         }
 220  
 
 221  1
         render(definition, request);
 222  1
     }
 223  
 
 224  
     /**
 225  
      * Renders the specified definition.
 226  
      * @param definition The definition to render.
 227  
      * @param request The request context.
 228  
      * @since 2.1.3
 229  
      */
 230  
     public void render(Definition definition, Request request) {
 231  3
         AttributeContext originalContext = getAttributeContext(request);
 232  3
         BasicAttributeContext subContext = new BasicAttributeContext(originalContext);
 233  3
         subContext.inherit(definition);
 234  
 
 235  3
         pushContext(subContext, request);
 236  
 
 237  
         try {
 238  3
             render(request, subContext);
 239  
         } finally {
 240  3
             popContext(request);
 241  2
         }
 242  2
     }
 243  
 
 244  
     /** {@inheritDoc} */
 245  
     public void render(Attribute attr, Request request)
 246  
         throws IOException {
 247  14
         if (attr == null) {
 248  1
             throw new CannotRenderException("Cannot render a null attribute");
 249  
         }
 250  
 
 251  13
         if (attr.isPermitted(request)) {
 252  12
             Renderer renderer = rendererFactory.getRenderer(attr.getRenderer());
 253  10
             Object value = evaluate(attr, request);
 254  10
             if (!(value instanceof String)) {
 255  2
                 throw new CannotRenderException(
 256  
                         "Cannot render an attribute that is not a string, toString returns: "
 257  
                                 + value);
 258  
             }
 259  8
             renderer.render((String) value, request);
 260  
         }
 261  7
     }
 262  
 
 263  
     /** {@inheritDoc} */
 264  
     public Object evaluate(Attribute attribute, Request request) {
 265  12
         AttributeEvaluator evaluator = attributeEvaluatorFactory
 266  
                 .getAttributeEvaluator(attribute);
 267  12
         return evaluator.evaluate(attribute, request);
 268  
     }
 269  
 
 270  
     /** {@inheritDoc} */
 271  
     public boolean isValidDefinition(String definitionName, Request request) {
 272  
         try {
 273  3
             Definition definition = getDefinition(definitionName, request);
 274  2
             return definition != null;
 275  1
         } catch (NoSuchDefinitionException nsde) {
 276  1
             log.debug("Cannot find definition '{}'", definitionName);
 277  1
             log.debug("Exception related to the not found definition", nsde);
 278  1
             return false;
 279  
         }
 280  
     }
 281  
 
 282  
     /** {@inheritDoc} */
 283  
     @Override
 284  
     public Definition getDefinition(String definitionName,
 285  
             Request request) {
 286  6
         Definition definition =
 287  
             definitionsFactory.getDefinition(definitionName, request);
 288  5
         return definition;
 289  
     }
 290  
 
 291  
     /**
 292  
      * Returns the context stack.
 293  
      *
 294  
      * @param tilesContext The Tiles context object to use.
 295  
      * @return The needed stack of contexts.
 296  
      * @since 2.0.6
 297  
      */
 298  
     @SuppressWarnings("unchecked")
 299  
     protected Deque<AttributeContext> getContextStack(Request tilesContext) {
 300  22
         Map<String, Object> requestScope = tilesContext.getContext("request");
 301  22
         Deque<AttributeContext> contextStack = (Deque<AttributeContext>) requestScope
 302  
                 .get(ATTRIBUTE_CONTEXT_STACK);
 303  22
         if (contextStack == null) {
 304  1
             contextStack = new LinkedList<AttributeContext>();
 305  1
             requestScope.put(ATTRIBUTE_CONTEXT_STACK, contextStack);
 306  
         }
 307  
 
 308  22
         return contextStack;
 309  
     }
 310  
 
 311  
     /**
 312  
      * Pushes a context object in the stack.
 313  
      *
 314  
      * @param context The context to push.
 315  
      * @param tilesContext The Tiles context object to use.
 316  
      * @since 2.0.6
 317  
      */
 318  
     protected void pushContext(AttributeContext context,
 319  
             Request tilesContext) {
 320  5
         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
 321  5
         contextStack.push(context);
 322  5
     }
 323  
 
 324  
     /**
 325  
      * Pops a context object out of the stack.
 326  
      *
 327  
      * @param tilesContext The Tiles context object to use.
 328  
      * @return The popped context object.
 329  
      * @since 2.0.6
 330  
      */
 331  
     protected AttributeContext popContext(Request tilesContext) {
 332  5
         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
 333  5
         return contextStack.pop();
 334  
     }
 335  
 
 336  
     /**
 337  
      * Get attribute context from request.
 338  
      *
 339  
      * @param tilesContext current Tiles application context.
 340  
      * @return BasicAttributeContext or null if context is not found.
 341  
      * @since 2.0.6
 342  
      */
 343  
     protected AttributeContext getContext(Request tilesContext) {
 344  9
         Deque<AttributeContext> contextStack = getContextStack(tilesContext);
 345  9
         if (!contextStack.isEmpty()) {
 346  7
             return contextStack.peek();
 347  
         }
 348  2
         return null;
 349  
     }
 350  
 
 351  
     /**
 352  
      * Execute a preparer.
 353  
      *
 354  
      * @param context The request context.
 355  
      * @param preparerName The name of the preparer.
 356  
      * @param ignoreMissing If <code>true</code> if the preparer is not found,
 357  
      * it ignores the problem.
 358  
      * @throws NoSuchPreparerException If the preparer is not found (and
 359  
      * <code>ignoreMissing</code> is not set) or if the preparer itself threw an
 360  
      * exception.
 361  
      */
 362  
     private void prepare(Request context, String preparerName, boolean ignoreMissing) {
 363  
 
 364  3
         log.debug("Prepare request received for '{}'", preparerName);
 365  
 
 366  3
         ViewPreparer preparer = preparerFactory.getPreparer(preparerName, context);
 367  3
         if (preparer == null && ignoreMissing) {
 368  1
             return;
 369  
         }
 370  
 
 371  2
         if (preparer == null) {
 372  1
             throw new NoSuchPreparerException("Preparer '" + preparerName + " not found");
 373  
         }
 374  
 
 375  1
         AttributeContext attributeContext = getContext(context);
 376  
 
 377  1
         preparer.execute(context, attributeContext);
 378  1
     }
 379  
 
 380  
     /**
 381  
      * Renders the specified attribute context.
 382  
      *
 383  
      * @param request The request context.
 384  
      * @param attributeContext The context to render.
 385  
      * @throws InvalidTemplateException If the template is not valid.
 386  
      * @throws CannotRenderException If something goes wrong during rendering.
 387  
      * @since 2.1.3
 388  
      */
 389  
     protected void render(Request request,
 390  
             AttributeContext attributeContext) {
 391  
 
 392  
         try {
 393  6
             if (attributeContext.getPreparer() != null) {
 394  1
                 prepare(request, attributeContext.getPreparer(), true);
 395  
             }
 396  
 
 397  6
             render(attributeContext.getTemplateAttribute(), request);
 398  2
         } catch (IOException e) {
 399  2
             throw new CannotRenderException(e.getMessage(), e);
 400  4
         }
 401  4
     }
 402  
 }