Coverage Report - org.apache.tiles.impl.mgmt.CachingTilesContainer
 
Classes in this File Line Coverage Branch Coverage Complexity
CachingTilesContainer
100%
57/57
88%
23/26
2.636
 
 1  
 /*
 2  
  * $Id: CachingTilesContainer.java 1045365 2010-12-13 20:46:46Z apetrelli $
 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.mgmt;
 22  
 
 23  
 import java.util.HashMap;
 24  
 import java.util.Map;
 25  
 
 26  
 import org.apache.tiles.Definition;
 27  
 import org.apache.tiles.TilesContainer;
 28  
 import org.apache.tiles.TilesContainerWrapper;
 29  
 import org.apache.tiles.definition.NoSuchDefinitionException;
 30  
 import org.apache.tiles.mgmt.MutableTilesContainer;
 31  
 import org.apache.tiles.request.Request;
 32  
 
 33  
 /**
 34  
  * Manages custom and configured definitions, so they can be used by the
 35  
  * container, instead of using a simple {@link org.apache.tiles.definition.DefinitionsFactory}.
 36  
  *
 37  
  * @version $Rev: 1045365 $ $Date: 2010-12-14 07:46:46 +1100 (Tue, 14 Dec 2010) $
 38  
  */
 39  
 public class CachingTilesContainer extends TilesContainerWrapper implements MutableTilesContainer {
 40  
 
 41  
     /**
 42  
      * The default name of the attribute in which storing custom definitions.
 43  
      */
 44  
     private static final String DEFAULT_DEFINITIONS_ATTRIBUTE_NAME =
 45  
         "org.apache.tiles.impl.mgmt.DefinitionManager.DEFINITIONS";
 46  
 
 47  
     /**
 48  
      * The name of the attribute in which storing custom definitions.
 49  
      */
 50  
     private String definitionsAttributeName;
 51  
 
 52  
     /**
 53  
      * Constructor.
 54  
      * @param originalContainer The original container to wrap.
 55  
      */
 56  
     public CachingTilesContainer(TilesContainer originalContainer) {
 57  12
         super(originalContainer);
 58  12
         definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
 59  12
     }
 60  
 
 61  
     /**
 62  
      * Constructor.
 63  
      * @param originalContainer The original container to wrap.
 64  
      * @param definitionsAttributeName The name of the attribute in which
 65  
      * storing custom definitions.
 66  
      */
 67  
     public CachingTilesContainer(TilesContainer originalContainer, String definitionsAttributeName) {
 68  2
         super(originalContainer);
 69  2
         this.definitionsAttributeName = definitionsAttributeName;
 70  2
         if (this.definitionsAttributeName == null) {
 71  1
             this.definitionsAttributeName = DEFAULT_DEFINITIONS_ATTRIBUTE_NAME;
 72  
         }
 73  2
     }
 74  
 
 75  
     /**
 76  
      * Returns a definition by name.
 77  
      *
 78  
      * @param definition The name of the definition.
 79  
      * @param request The current request.
 80  
      * @return The requested definition, either main or custom.
 81  
      * @throws org.apache.tiles.definition.DefinitionsFactoryException If
 82  
      * something goes wrong when obtaining a main definition.
 83  
      */
 84  
     public Definition getDefinition(String definition,
 85  
             Request request) {
 86  6
         Definition retValue = null;
 87  6
         retValue = getCustomDefinition(definition, request);
 88  6
         if (retValue == null) {
 89  2
             retValue = super.getDefinition(definition, request);
 90  
         }
 91  6
         return retValue;
 92  
     }
 93  
 
 94  
     /** {@inheritDoc} */
 95  
     @Override
 96  
     public boolean isValidDefinition(String definition, Request request) {
 97  2
         if (getCustomDefinition(definition, request) != null) {
 98  1
             return true;
 99  
         }
 100  1
         return super.isValidDefinition(definition, request);
 101  
     }
 102  
 
 103  
     /** {@inheritDoc} */
 104  
     @Override
 105  
     public void register(Definition definition, Request request) {
 106  4
         Map<String, Definition> definitions = getOrCreateDefinitions(request);
 107  4
         if (definition.getName() == null) {
 108  4
             definition.setName(getNextUniqueDefinitionName(definitions));
 109  
         }
 110  
 
 111  4
         if (definition.isExtending()) {
 112  4
             this.resolveInheritance(definition, request);
 113  
         }
 114  
 
 115  3
         definitions.put(definition.getName(), definition);
 116  3
     }
 117  
 
 118  
     /** {@inheritDoc} */
 119  
     @Override
 120  
     public void render(String definition, Request request) {
 121  2
         Definition toRender = getDefinition(definition, request);
 122  2
         if (toRender == null) {
 123  1
             throw new NoSuchDefinitionException(
 124  
                     "Cannot find definition named '" + definition + "'");
 125  
         }
 126  1
         super.render(toRender, request);
 127  1
     }
 128  
 
 129  
     /**
 130  
      * Resolve inheritance.
 131  
      * First, resolve parent's inheritance, then set template to the parent's
 132  
      * template.
 133  
      * Also copy attributes set in parent, and not set in child
 134  
      * If instance doesn't extend anything, do nothing.
 135  
      *
 136  
      * @param definition The definition that needs to have its inheritances
 137  
      * resolved.
 138  
      * @param request The current request.
 139  
      * @throws org.apache.tiles.definition.DefinitionsFactoryException If an
 140  
      * inheritance can not be solved.
 141  
      */
 142  
     private void resolveInheritance(Definition definition,
 143  
             Request request) {
 144  
         // Already done, or not needed ?
 145  5
         if (!definition.isExtending()) {
 146  2
             return;
 147  
         }
 148  
 
 149  3
         String parentDefinitionName = definition.getExtends();
 150  
 
 151  3
         boolean recurse = true;
 152  3
         Definition parent = getCustomDefinition(parentDefinitionName, request);
 153  3
         if (parent == null) {
 154  2
             parent = container.getDefinition(parentDefinitionName, request);
 155  2
             recurse = false;
 156  
         }
 157  
 
 158  3
         if (parent == null) {
 159  1
             throw new NoSuchDefinitionException(
 160  
                     "Error while resolving definition inheritance: child '"
 161  
                             + definition.getName()
 162  
                             + "' can't find its ancestor '"
 163  
                             + parentDefinitionName
 164  
                             + "'. Please check your description file.");
 165  
         }
 166  
 
 167  
         // Resolve parent before itself.
 168  2
         if (recurse) {
 169  1
             resolveInheritance(parent, request);
 170  
         }
 171  2
         definition.inherit(parent);
 172  2
     }
 173  
 
 174  
     /**
 175  
      * Returns the map with custom definitions for the current request.
 176  
      *
 177  
      * @param request The current request.
 178  
      * @return A map that connects a definition name to a definition.
 179  
      */
 180  
     @SuppressWarnings("unchecked")
 181  
     private Map<String, Definition> getDefinitions(
 182  
             Request request) {
 183  11
         return (Map<String, Definition>) request.getContext("request")
 184  
                 .get(definitionsAttributeName);
 185  
     }
 186  
 
 187  
     /**
 188  
      * Returns a map of type "definition name -> definition" and, if it has not
 189  
      * been defined before, creates one.
 190  
      *
 191  
      * @param request The current request.
 192  
      * @return A map that connects a definition name to a definition.
 193  
      */
 194  
     @SuppressWarnings("unchecked")
 195  
     private Map<String, Definition> getOrCreateDefinitions(
 196  
             Request request) {
 197  4
         Map<String, Definition> definitions =
 198  
             (Map<String, Definition>) request.getContext("request").get(definitionsAttributeName);
 199  4
         if (definitions == null) {
 200  1
             definitions = new HashMap<String, Definition>();
 201  1
             request.getContext("request")
 202  
                     .put(definitionsAttributeName, definitions);
 203  
         }
 204  
 
 205  4
         return definitions;
 206  
     }
 207  
 
 208  
     /**
 209  
      * Create a unique definition name usable to store anonymous definitions.
 210  
      *
 211  
      * @param definitions The already created definitions.
 212  
      * @return The unique definition name to be used to store the definition.
 213  
      * @since 2.1.0
 214  
      */
 215  
     private String getNextUniqueDefinitionName(
 216  
             Map<String, Definition> definitions) {
 217  
         String candidate;
 218  4
         int anonymousDefinitionIndex = 1;
 219  
 
 220  
         do {
 221  4
             candidate = "$anonymousMutableDefinition" + anonymousDefinitionIndex;
 222  4
             anonymousDefinitionIndex++;
 223  4
         } while (definitions.containsKey(candidate));
 224  
 
 225  4
         return candidate;
 226  
     }
 227  
 
 228  
     /**
 229  
      * Returns a custom definition from the cache.
 230  
      *
 231  
      * @param definition The definition to search.
 232  
      * @param request The request.
 233  
      * @return The requested definition.
 234  
      */
 235  
     private Definition getCustomDefinition(String definition, Request request) {
 236  11
         Map<String, Definition> definitions = getDefinitions(request);
 237  11
         if (definitions != null) {
 238  10
             return definitions.get(definition);
 239  
         }
 240  1
         return null;
 241  
     }
 242  
 }