2009/05/20 - Apache Shale has been retired.

For more information, please explore the Attic.

Coverage Report - org.apache.shale.clay.config.beans.TemplateConfigBean
 
Classes in this File Line Coverage Branch Coverage Complexity
TemplateConfigBean
100%
93/93
N/A
4.6
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one or more
 3  
  * contributor license agreements.  See the NOTICE file distributed with
 4  
  * this work for additional information regarding copyright ownership.
 5  
  * The ASF licenses this file to you under the Apache License, Version 2.0
 6  
  * (the "License"); you may not use this file except in compliance with
 7  
  * the License.  You may obtain a copy of the License at
 8  
  *
 9  
  *      http://www.apache.org/licenses/LICENSE-2.0
 10  
  *
 11  
  * Unless required by applicable law or agreed to in writing, software
 12  
  * distributed under the License is distributed on an "AS IS" BASIS,
 13  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14  
  * See the License for the specific language governing permissions and
 15  
  * limitations under the License.
 16  
  */
 17  
 
 18  
 /*
 19  
  * $Id: TemplateConfigBean.java 464373 2006-10-16 04:21:54Z rahul $
 20  
  */
 21  
 package org.apache.shale.clay.config.beans;
 22  
 
 23  
 import java.util.BitSet;
 24  
 import java.util.Iterator;
 25  
 import java.util.Map;
 26  
 import java.util.TreeMap;
 27  
 
 28  
 import javax.servlet.ServletContext;
 29  
 
 30  
 import org.apache.shale.clay.config.ClayTemplateParser;
 31  
 import org.apache.shale.clay.config.Globals;
 32  
 
 33  
 /**
 34  
  * <p>The second type of top-level object pool.  This implementation
 35  
  * is designed to provide Tapestry like template composition.  The
 36  
  * top-level {@link ComponentBean} is materialized from a HTML fragment
 37  
  * where HTML elements are bound to meta components defined in the
 38  
  * XML configuration files and cached by an instance of {@link ComponentConfigBean}
 39  
  * </p>
 40  
  */
 41  96
 public class TemplateConfigBean extends ComponentConfigBean {
 42  
 
 43  
     /**
 44  
      * <p>Returns a {@link ComponentBean} that is materialized
 45  
      * using a HTML template fragment.  The <code>templateName</code>
 46  
      * is the name of the file relative to the context root of the
 47  
      * web application</p>
 48  
      * @param templateName name of the markup template
 49  
      * @return root component bean for the <code>templateName</code>
 50  
      */
 51  
     public ComponentBean getElement(String templateName) {
 52  
 
 53  35
         StringBuffer jsfid = new StringBuffer(templateName);
 54  35
         if (jsfid.length() > 0 &&  jsfid.charAt(0) != '/') {
 55  2
           jsfid.insert(0, '/');
 56  
         }
 57  
 
 58  
         // look for a watcher identified by the template name
 59  35
         WatchDog watchDog = (WatchDog) watchDogs.get(jsfid.toString());
 60  
 
 61  
         //if a watcher doesn't exist, check for a common
 62  35
         if (watchDog == null) {
 63  35
             watchDog = (WatchDog) watchDogs.get(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG);
 64  
         }
 65  
 
 66  35
         if (watchDog == null || super.getElement(jsfid.toString()) == null) {
 67  
            //The first time the page is created, create a watcher
 68  
 
 69  33
            watchDog = new WatchDog(getConfigDefinitions(jsfid.toString()), jsfid.toString());
 70  
 
 71  
            // register by name
 72  33
            watchDogs.put(watchDog.getName(), watchDog);
 73  
 
 74  
            //loads the HTML template the first time and when file
 75  
            //has been modified
 76  33
            watchDog.refresh(false);
 77  32
            } else {
 78  
            //check to see if an existing html template
 79  
            //needs reloaded
 80  2
            watchDog.refresh(false);
 81  
         }
 82  
 
 83  
         // returns the cached element
 84  34
         return super.getElement(jsfid.toString());
 85  
     }
 86  
 
 87  
 
 88  
     /**
 89  
      * <p>Returns an integer value use to order the registered {@link ConfigBean} instances
 90  
      * with the {@link ConfigBeanFactory}.
 91  
      * </p>
 92  
      *
 93  
      * @return weight value of <code>1</code>
 94  
      */
 95  
     public int getWeight() {
 96  96
         return 1;
 97  
     }
 98  
 
 99  
     /**
 100  
      * <p>Overrides the super call to change the condition of the filter.  This
 101  
      * {@link ConfigBean} can create components where the id end in the suffix
 102  
      * defined in the web deployment descriptor as a initialization parameter with
 103  
      * the name defined by <code>Globals.CLAY_HTML_TEMPLATE_SUFFIX</code>  Or, using
 104  
      * the default defined by <code>Globals.CLAY_DEFAULT_HTML_TEMPLATE_SUFFIX</code>
 105  
      *
 106  
      * @param id jsfid
 107  
      * @return <code>true</code> if the <code>jsfid</code> can be handled here
 108  
      */
 109  
     public boolean validMoniker(String id) {
 110  661
         return id.endsWith(suffixes[0]);
 111  
     }
 112  
 
 113  
     /**
 114  
      * <p>This is an overridden method called from the init method.
 115  
      * It loads an instance of the {@link ClayTemplateParser} and
 116  
      * establishes a Map collection to hold the resource
 117  
      * {@link org.apache.shale.clay.config.beans.ComponentConfigBean$WatchDog}'s.</p>
 118  
      */
 119  
     protected void loadConfigFiles() {
 120  40
         parser = new ClayTemplateParser();
 121  40
         parser.setConfig(this);
 122  
 
 123  40
         watchDogs = new TreeMap();
 124  40
     }
 125  
 
 126  
 
 127  
     /**
 128  
      * <p>The HTML templates are loaded on-demand.  Override this method forcing the auto
 129  
      * load option on.  The XML configuration files are only effected by the
 130  
      * <code>auto-reload-clay-files</code> initialization parameter.</p>
 131  
      *
 132  
      * @param context web container servlet context
 133  
      */
 134  
     public void init(ServletContext context) {
 135  80
         super.init(context);
 136  
 
 137  80
         isWatchDogOn = true;
 138  80
     }
 139  
 
 140  
 
 141  
     /**
 142  
      * <p>If the <code>watchDogName</code> equals
 143  
      * the {@link ComponentBean} that defines the selected
 144  
      * template, remove it.</p>
 145  
      *
 146  
      * @param watchDogName grouping of template files
 147  
      */
 148  
     protected void clear(String watchDogName) {
 149  130
         if (!watchDogName.equals(Globals.DEFAULT_COMPONENT_CONFIG_WATCHDOG)) {
 150  
             //unassign a single template component
 151  33
             ComponentBean b = (ComponentBean) displayElements.get(watchDogName);
 152  33
             displayElements.remove(watchDogName);
 153  33
             if (b != null) {
 154  
                 try {
 155  
                     unassignParent(b);
 156  
                 } catch (RuntimeException e1) {
 157  
                     // log.error(e1);
 158  
                 }
 159  
             }
 160  33
             b = null;
 161  33
         } else {
 162  97
            super.clear(watchDogName);
 163  
         }
 164  130
     }
 165  
 
 166  
 
 167  
     /**
 168  
      * <p>If the <code>forceReload</code> is <code>true</code>,
 169  
      * the <code>displayElements</code> cache is invalidated.
 170  
      * A <code>true</code> value is returned if cache has
 171  
      * been cleared.</p>
 172  
      *
 173  
      * @param forceReload invalidate the cache flag
 174  
      * @return <code>true</code> if the templates were reloaded
 175  
      */
 176  
     public boolean refresh(boolean forceReload) {
 177  
         if (forceReload) {
 178  
             //synchronized (displayElements) {
 179  
 
 180  
                 //remove all old templates
 181  
                 Iterator wi = watchDogs.entrySet().iterator();
 182  
                 while (wi.hasNext()) {
 183  
                     Map.Entry e = (Map.Entry) wi.next();
 184  
                     WatchDog watchDog = (WatchDog) e.getValue();
 185  
                     clear(watchDog.getName());
 186  
                     if (watchDog != null) {
 187  
                         watchDog.destroy();
 188  
                     }
 189  
 
 190  
                 }
 191  
                 watchDogs.clear();
 192  
             //}
 193  
         }
 194  
 
 195  
         return forceReload;
 196  
     }
 197  
 
 198  
 
 199  
     /**
 200  
      * <p>
 201  
      * Returns the root metadata component that is used to add to the component
 202  
      * tree. It locates the {@link ComponentBean} using the <code>jsfid</code>
 203  
      * attribute as the key. A call to the {@link ConfigBeanFactory} locates the
 204  
      * correct {@link ConfigBean} used to find the {@link ComponentBean}. </p>
 205  
      *
 206  
      * @param jsfid parent id of a config bean
 207  
      * @return parent config bean
 208  
      */
 209  
     protected ComponentBean getTopLevelElement(String jsfid) {
 210  
 
 211  643
         if (validMoniker(jsfid)) {
 212  1
             return getElement(jsfid);
 213  
         }
 214  
 
 215  
         //broaden the search to the other ConfigBean's
 216  642
         ConfigBean config = ConfigBeanFactory.findConfig(jsfid);
 217  
 
 218  642
         if (config == null) {
 219  
             throw new NullPointerException(messages
 220  
                     .getMessage("config.notloaded", new Object[] { jsfid }));
 221  
         }
 222  
 
 223  642
         if (config == this) {
 224  
             throw new NullPointerException(messages.getMessage(
 225  
                     "jsfid.notfound", new Object[] { jsfid }));
 226  
         }
 227  
 
 228  
         // find the top-level display element associated with the subtree
 229  642
         ComponentBean b = config.getElement(jsfid);
 230  642
         if (b == null) {
 231  
             throw new NullPointerException(messages.getMessage(
 232  
                     "jsfid.notfound", new Object[] { jsfid }));
 233  
         }
 234  
 
 235  642
         return b;
 236  
     }
 237  
 
 238  
 
 239  
     /**
 240  
      * <p>Determines if the <code>node</code> is a transient
 241  
      * <code>outputText</code> (<strong>verbatim</strong>) component.</p>
 242  
      *
 243  
      * @param node a config bean that represents a template token
 244  
      * @return <code>true</code> if the node is a verbatim node
 245  
      */
 246  
     private boolean isVerbatim(ComponentBean node) {
 247  
 
 248  1252
         AttributeBean attr = null;
 249  1252
         if ((node.getJsfid().equals("verbatim") || node.getJsfid().equals("f:verbatim"))
 250  
              && node.getComponentType().equals("javax.faces.HtmlOutputText")) {
 251  847
             attr = node.getAttribute("isTransient");
 252  847
             if (attr != null) {
 253  847
                 if (attr.getValue() != null && attr.getValue().length() > 0) {
 254  847
                     return (Character.toLowerCase(attr.getValue().charAt(0)) == 't');
 255  
                 }
 256  
             }
 257  
         }
 258  
 
 259  405
         return false;
 260  
     }
 261  
 
 262  
 
 263  
     /**
 264  
      * <p>Recursively walks down the graph of meta-data {@link ComponentBean}'s
 265  
      * looking at the children of the <code>root</code>.  Adjacent
 266  
      * children that are both <code>verbatim</code> component
 267  
      * definitions are merged.  If there is only one child and
 268  
      * the child and root nodes are both <code>verbatim</code>
 269  
      * definitions, the child is merged up to the root.</p>
 270  
      *
 271  
      * @param root top config bean that represents a markup template
 272  
      */
 273  
     public void optimizeTree(ComponentBean root) {
 274  
 
 275  
         // children is a TreeSet that is returned as a Collection.
 276  120
         int size = root.getChildren().size();
 277  120
         ComponentBean[] children = new ComponentBean[size];
 278  120
         BitSet verbatimSet = new BitSet(size);
 279  120
         verbatimSet.clear(0, size);
 280  
 
 281  120
         StringBuffer buff = new StringBuffer();
 282  
 
 283  120
         int i = 0;
 284  120
         Iterator ci = root.getChildrenIterator();
 285  1249
         while (ci.hasNext()) {
 286  1129
             children[i] = (ComponentBean) ci.next();
 287  1129
             if (isVerbatim(children[i])) {
 288  841
                 verbatimSet.set(i);
 289  
             }
 290  
 
 291  1129
             if (children[i].getChildren().size() > 0) {
 292  86
                 optimizeTree(children[i]);    // merge children for the top down
 293  
                 // starting a the botton of the tree.
 294  
             }
 295  
 
 296  1129
             i++;
 297  1129
         }
 298  
 
 299  120
         int s = -1;
 300  557
         while ((s = verbatimSet.nextSetBit(++s)) > -1) {
 301  
 
 302  1253
             merge: for (int j = s + 1; j < children.length; j++) {
 303  1062
                 if (verbatimSet.get(j)) {
 304  816
                     buff.setLength(0);
 305  
 
 306  
                     // grap the value attribute of the first one in the stack
 307  
                     // and concat to a buffer
 308  816
                     AttributeBean attrTop = children[s].getAttribute("value");
 309  816
                     if (attrTop != null) {
 310  816
                         if (attrTop.getValue() != null) {
 311  816
                             buff.append(attrTop.getValue());
 312  
                         }
 313  
                     } else {
 314  
                         break merge;   // a verbatim without a value should never happen
 315  
                     }
 316  
 
 317  816
                     AttributeBean attrNext = children[j].getAttribute("value");  // the next in sequence to be merged
 318  816
                     if (attrNext != null) {
 319  816
                         if (attrNext.getValue() != null) {
 320  816
                             buff.append(attrNext.getValue());
 321  
                         }
 322  
                     } else {
 323  
                         continue merge;   // a verbatim without a value should never happen
 324  
                     }
 325  
                     // merge node values
 326  816
                     attrTop.setValue(buff.toString());
 327  816
                     root.getChildren().remove(children[j]); // delete the node after merge from the parent
 328  
 
 329  816
                 } else {
 330  
                     // the verbatims are not in sequence (true, false, true)
 331  246
                     s = j;
 332  246
                     break merge;
 333  
                 }
 334  
             }
 335  
 
 336  
 
 337  191
         }
 338  
 
 339  
         // if the root is a verbatim and the only child is a verbatim
 340  
         // merge up to the root
 341  120
         if (isVerbatim(root) && root.getChildren().size() == 1
 342  
                 && isVerbatim(children[0])) {
 343  
 
 344  3
             buff.setLength(0);
 345  
 
 346  
             // grap the value attribute of the first one in the stack
 347  
             // and concat to a buffer
 348  3
             AttributeBean attrTop = root.getAttribute("value");
 349  3
             if (attrTop != null) {
 350  3
                 if (attrTop.getValue() != null) {
 351  3
                     buff.append(attrTop.getValue());
 352  
                 }
 353  
 
 354  3
                 AttributeBean attrNext = children[0].getAttribute("value");  // the next in sequence to be merged
 355  3
                 if (attrNext != null) {
 356  3
                     if (attrNext.getValue() != null) {
 357  3
                         buff.append(attrNext.getValue());
 358  
                     }
 359  
                 }
 360  
                 // merge node values
 361  3
                 attrTop.setValue(buff.toString());
 362  3
                 root.getChildren().clear(); // delete the node after merge from the parent
 363  
             }
 364  
         }
 365  
 
 366  
 
 367  120
     }
 368  
 
 369  
 }