Coverage Report - net.sf.tacos.components.tree.Tree
 
Classes in this File Line Coverage Branch Coverage Complexity
Tree
0% 
0% 
1,622
 
 1  
 package net.sf.tacos.components.tree;
 2  
 
 3  
 import java.io.Serializable;
 4  
 import java.util.ArrayList;
 5  
 import java.util.Collection;
 6  
 import java.util.Collections;
 7  
 import java.util.Comparator;
 8  
 import java.util.HashMap;
 9  
 import java.util.List;
 10  
 import java.util.Map;
 11  
 import java.util.Set;
 12  
 import java.util.Stack;
 13  
 import net.sf.tacos.model.IKeyProvider;
 14  
 import net.sf.tacos.model.ITreeContentProvider;
 15  
 import net.sf.tacos.model.IdentityKeyProvider;
 16  
 import org.apache.commons.lang.StringEscapeUtils;
 17  
 import org.apache.commons.logging.Log;
 18  
 import org.apache.commons.logging.LogFactory;
 19  
 import org.apache.tapestry.BaseComponent;
 20  
 import org.apache.tapestry.IActionListener;
 21  
 import org.apache.tapestry.IDirect;
 22  
 import org.apache.tapestry.IMarkupWriter;
 23  
 import org.apache.tapestry.IRequestCycle;
 24  
 import org.apache.tapestry.IScript;
 25  
 import org.apache.tapestry.PageRenderSupport;
 26  
 import org.apache.tapestry.TapestryUtils;
 27  
 import org.apache.tapestry.bean.EvenOdd;
 28  
 import org.apache.tapestry.components.Any;
 29  
 import org.apache.tapestry.engine.DirectServiceParameter;
 30  
 import org.apache.tapestry.engine.IEngineService;
 31  
 import org.apache.tapestry.listener.ListenerInvoker;
 32  
 
 33  
 /**
 34  
  * Base component for providing Tree-like semantics for displaying data.
 35  
  * 
 36  
  * @author phraktle
 37  
  */
 38  0
 public abstract class Tree extends BaseComponent implements /*PartialRenderBlock, */IDirect {
 39  
 
 40  
     /** Logger */
 41  0
     private static final Log log = LogFactory.getLog(Tree.class);
 42  
     /** Default key provider */
 43  0
     private static final IKeyProvider identityKeyProvider = new IdentityKeyProvider();
 44  
     /** Default iterator */
 45  
     private TreeIterator treeIterator;
 46  
     /** Parent nodes */
 47  
     private Stack parts;
 48  
     
 49  
     /**
 50  
      * The default {@link IdentityKeyProvider}.
 51  
      * 
 52  
      * @return The default key provider.
 53  
      */
 54  
     public IKeyProvider getIdentityKeyProvider()
 55  
     {
 56  0
         return identityKeyProvider;
 57  
     }
 58  
     
 59  
     /**
 60  
      * The default {@link TreeIterator} provided if no other is specified.
 61  
      * 
 62  
      * @return Instance of {@link TreeIterator}
 63  
      */
 64  
     public TreeIterator getTreeIterator()
 65  
     {
 66  0
         return treeIterator;
 67  
     }
 68  
     
 69  
     /**
 70  
      * {@inheritDoc}
 71  
      */
 72  
     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
 73  
     {
 74  0
         boolean dynamic = cycle.getResponseBuilder().isDynamic();
 75  
         //Only if this is a window load and delayed loading is needed
 76  0
         if (!cycle.isRewinding() 
 77  
                 && isDelayedLoad()
 78  
                 && !dynamic/*getAjaxWebRequest().isValidRequest()*/) {            
 79  0
             renderDynamicLoad(writer, cycle);
 80  0
             return;
 81  
         }        
 82  0
         treeIterator = new TreeIter();
 83  0
         parts = new Stack();
 84  
         try {
 85  0
             super.renderComponent(writer, cycle);
 86  
         } finally {
 87  0
             try {
 88  0
                 while(!parts.isEmpty()) {
 89  0
                     String partId = parts.pop().toString();
 90  
                     /*if (getAjaxWebRequest().isValidRequest()
 91  
                             && cycle.getResponseBuilder().componentWriterExists(partId))
 92  
                         getAjaxWebRequest().getResponseBuilder().getComponentWriter(partId).end();
 93  
                     else
 94  
                         writer.end();*/
 95  0
                 }
 96  0
             } catch (Throwable t) {
 97  0
                 t.printStackTrace();
 98  0
             }
 99  0
             treeIterator = null;
 100  0
             parts = null;
 101  0
         }
 102  0
     }
 103  
     
 104  
     private void renderDynamicLoad(IMarkupWriter writer, IRequestCycle cycle) {
 105  
         //Render our node manually instead of using Any
 106  
         //we still have to have a node to replace on the client
 107  0
         Any anyContainer = (Any)getComponent("treeDiv");
 108  0
         writer.begin(anyContainer.getElement());
 109  0
         super.renderInformalParameters(writer, cycle);
 110  0
         writer.end();
 111  
 
 112  0
         String treeId = getClientId();
 113  
 
 114  
         /*AjaxDirectServiceParameter dsp = 
 115  
             new AjaxDirectServiceParameter(this, new Object[0], 
 116  
                     new String[] {treeId}, false);*/
 117  
 
 118  0
         DirectServiceParameter dsp = new DirectServiceParameter(this);
 119  
 
 120  0
         Map parms = new HashMap();
 121  0
         parms.put("treeId", treeId);
 122  0
         parms.put("url", StringEscapeUtils.escapeJavaScript(
 123  
                         getDirectService().getLink(true, dsp).getAbsoluteURL()));
 124  0
         if (getLoadElement() != null)
 125  0
             parms.put("loadElement", getLoadElement());
 126  
 
 127  0
         PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
 128  0
         getScript().execute(this, cycle, pageRenderSupport, parms);        
 129  0
     }
 130  
 
 131  
     /**
 132  
      * Called to request the expansion of a tree node.
 133  
      * 
 134  
      * @param cycle
 135  
      */
 136  
     public void expansion(IRequestCycle cycle)
 137  
     {
 138  0
         Object[] params = cycle.getListenerParameters();
 139  0
         Serializable key = (Serializable)params[0];
 140  0
         boolean expanded = ((Boolean)params[1]).booleanValue();        
 141  
         
 142  0
         getManager().setExpandedKey(key, expanded);
 143  0
         setState(getState());  
 144  0
     }
 145  
 
 146  
     /**
 147  
      * Invoked by contentLinkToggle component, will invoke
 148  
      * {@link #expansion(IRequestCycle)} first before checking if the invoking
 149  
      * component specified an {@link IActionListener} and any optional
 150  
      * parameters it may need.
 151  
      * 
 152  
      * @param cycle
 153  
      */
 154  
     public void contentExpansion(IRequestCycle cycle)
 155  
     {
 156  
         // Call expansion first
 157  0
         expansion(cycle);
 158  
         
 159  
         // If they specified a listener we will now invoke it
 160  0
         if (getLinkListener() == null) {
 161  0
             log.warn("contentExpansion() called but no linkListener was specified on the tree. "
 162  
                             + "Use the linkListener parameter if you want your page/component to be invoked "
 163  
                             + "as well.");
 164  0
             return;
 165  
         }
 166  
         
 167  0
         getListenerInvoker().invokeListener(getLinkListener(), this, cycle);
 168  0
     }
 169  
 
 170  
     /**
 171  
      * @param element
 172  
      *            to test expanded of
 173  
      * @return True if element should be displayed/expanded.
 174  
      */
 175  
     public boolean isExpanded(Object element)
 176  
     {
 177  0
         return getManager().isExpanded(element);
 178  
     }
 179  
 
 180  
     /*
 181  
      * Default {@link TreeIterator} implementation.
 182  
      */
 183  0
     private final class TreeIter extends TreeIterator {
 184  
 
 185  
         /**
 186  
          * Creates new iterator
 187  
          *
 188  
          */
 189  
         private TreeIter()
 190  0
         {
 191  0
             super(sorted(getContentProvider().getElements()));
 192  0
         }
 193  
 
 194  
         /**
 195  
          * 
 196  
          * {@inheritDoc}
 197  
          */
 198  
         protected Collection getChildren(Object node)
 199  
         {
 200  0
             if (!isExpanded(node)) { return Collections.EMPTY_LIST; }
 201  0
             Collection coll = getContentProvider().getChildren(node);
 202  0
             return sorted(coll);
 203  
         }
 204  
         
 205  
     }
 206  
     
 207  
     /**
 208  
      * Default comparator/sorter for tree elements.
 209  
      * 
 210  
      * @param elements
 211  
      * @return
 212  
      */
 213  
     private Collection sorted(Collection elements)
 214  
     {
 215  0
         Comparator comp = getSorter();
 216  0
         if (comp == null) { return elements; }
 217  0
         List l = new ArrayList(elements);
 218  0
         Collections.sort(l, comp);
 219  0
         return l;
 220  
     }
 221  
 
 222  
     /**
 223  
      * @return The {@link ITreeManager} for this tree.
 224  
      */
 225  
     public ITreeManager getManager()
 226  
     {
 227  0
         return new TreeManager(getState(), getContentProvider(),
 228  
                 getKeyProvider());
 229  
     }
 230  
     
 231  
     /**
 232  
      * {@inheritDoc}
 233  
      */
 234  
     public void beforeRenderBody(IMarkupWriter writer, IRequestCycle cycle,
 235  
             Serializable partId, boolean isRendering)
 236  
     {
 237  
         //If we need to close a parent
 238  0
         if (treeIterator.getDepth() <= treeIterator.getPreviousDepth()
 239  
                 && !parts.isEmpty()) {
 240  
             
 241  0
             int pops = 1;
 242  0
             if (treeIterator.getDepth() < treeIterator.getPreviousDepth()) {
 243  0
                 pops = treeIterator.getPreviousDepth()
 244  
                         - treeIterator.getDepth();
 245  0
                 pops++; // because iterator depth is 0-based
 246  
             }
 247  0
             while(pops > 0 && !parts.isEmpty()) {
 248  0
                 Serializable id = (Serializable)parts.pop();
 249  0
                 if (isRendering) {
 250  
                     
 251  
                     /*if (getAjaxWebRequest().isValidRequest()
 252  
                             && getAjaxWebRequest().containsComponentId(id.toString()))
 253  
                         getAjaxWebRequest().getResponseBuilder().getComponentWriter(id.toString()).end();
 254  
                     else*/ {
 255  0
                         IMarkupWriter pwriter = findParentPart();
 256  0
                         if (pwriter != null) pwriter.end();
 257  
                         else
 258  0
                             writer.end();
 259  
                     }
 260  
                 }
 261  0
                 pops--;
 262  0
             }
 263  
         }
 264  
         
 265  0
         if (isRendering) {
 266  0
             writer.begin("div");
 267  0
             writer.attribute("id", partId.toString());
 268  0
             if (isRowStyleInOuterDiv() && getRowStyle() != null)
 269  
             {
 270  0
                 writer.attribute("class", getRowStyle().getNext());
 271  
             }
 272  0
             else if (getPartialBlockClass() != null)
 273  
             {
 274  0
                 writer.attribute("class", getPartialBlockClass());
 275  
             }
 276  0
             parts.push(partId.toString());
 277  
         }
 278  0
     }
 279  
     
 280  
     /**
 281  
      * {@inheritDoc}
 282  
      */
 283  
     public void incrementNonRenderBlock()
 284  
     {
 285  0
         if (getRowStyle() != null) getRowStyle().getNext();
 286  0
         beforeRenderBody(null, null, getKeyProvider().getKey(getValue()), false);
 287  0
     }
 288  
     
 289  
     /**
 290  
      * {@inheritDoc}
 291  
      */
 292  
     public IMarkupWriter getPartWriter(Serializable partId)
 293  
     {
 294  0
         String partStr = partId.toString();
 295  
         // It may be specified directly
 296  0
         IMarkupWriter ret = null;
 297  
         
 298  
         // If we are not a root node we'll try
 299  
         // and find a matching parent
 300  0
         if (treeIterator.getDepth() < treeIterator.getPreviousDepth()
 301  
                 && !parts.isEmpty()
 302  
                 && ((parts.size() - 1) > (treeIterator.getPreviousDepth() - treeIterator
 303  
                         .getDepth()))) {
 304  0
             ret = findParentPart();
 305  0
         } else if (treeIterator.getDepth() > treeIterator.getPreviousDepth()
 306  
                 && !parts.isEmpty()) {
 307  0
             ret = findParentPart();
 308  0
         } else if (treeIterator.getDepth() == treeIterator.getPreviousDepth()
 309  
                 && parts.size() > 1) {
 310  0
             ret = findParentPart();
 311  
         }
 312  
         
 313  0
         return ret;
 314  
     }
 315  
     
 316  
     /**
 317  
      * Loops through the current part stack looking for a part that returns a
 318  
      * valid {@link IMarkupWriter}.
 319  
      * 
 320  
      * @return Valid writer, or null.
 321  
      */
 322  
     private IMarkupWriter findParentPart()
 323  
     {
 324  
         //if (!getAjaxWebRequest().isValidRequest())        
 325  
             //return null;
 326  
         
 327  0
         IMarkupWriter ret = null;
 328  0
         Serializable[] partIds = (Serializable[])parts
 329  
                 .toArray(new Serializable[parts.size()]);
 330  
         /*for(int i = 0; i < partIds.length; i++) {
 331  
             if (getAjaxWebRequest().containsComponentId(partIds[i].toString()))
 332  
                 return getAjaxWebRequest().getResponseBuilder().getComponentWriter(partIds[i].toString());
 333  
         }*/
 334  
         
 335  0
         return ret;
 336  
     }
 337  
     
 338  
     /**
 339  
      * Used to pass self into {@link PartialForeach}.
 340  
      * 
 341  
      * @return
 342  
      *//*
 343  
     public PartialRenderBlock getPartialTreeBlock()
 344  
     {
 345  
         return this;
 346  
     }*/
 347  
     
 348  
     public boolean getCurrentHasChildren() {
 349  0
         return getContentProvider().hasChildren(getValue());
 350  
     }
 351  
     
 352  
     public Serializable getCurrentKey() {
 353  0
         return getKeyProvider().getKey(getValue());
 354  
     }
 355  
     
 356  
     public boolean getCurrentNotExpanded() {
 357  0
         return !isExpanded(getValue());
 358  
     }
 359  
     
 360  
     public String getCurrentOffset() {
 361  0
         int off = (getContentProvider().hasChildren(getValue())) ? 0 : 1;
 362  0
         return "" + ( off + getTreeIterator().getDepth() ) * getOffset();
 363  
     }
 364  
     
 365  
     /**
 366  
      * {@inheritDoc}
 367  
      */
 368  
     public boolean isStateful()
 369  
     {
 370  0
         return true;
 371  
     }
 372  
     
 373  
     /**
 374  
      * {@inheritDoc}
 375  
      */
 376  
     public void trigger(IRequestCycle cycle)
 377  
     {
 378  
         //Does nothing, only used in delayed loads
 379  0
     }
 380  
 
 381  
     /** Injected script */
 382  
     public abstract IScript getScript();
 383  
     /** Injected ajax engine */
 384  
     public abstract IEngineService getDirectService();
 385  
     /** Current value being rendered */
 386  
     public abstract Object getValue();
 387  
     /** Gets default/specified state */
 388  
     public abstract Set getState();
 389  
     /** Saves tree state */
 390  
     public abstract void setState(Set state);
 391  
     /** Injected content provider */
 392  
     public abstract ITreeContentProvider getContentProvider();
 393  
     /** Injected key provider */
 394  
     public abstract IKeyProvider getKeyProvider();
 395  
     /** Injected sort {@link Comparator} */
 396  
     public abstract Comparator getSorter();
 397  
     /** Optional EvenOdd component */
 398  
     public abstract EvenOdd getRowStyle();
 399  
     /** The offset in pixels for depth-indentation */
 400  
     public abstract int getOffset();     
 401  
     /** Where to apply the row style */
 402  
     public abstract boolean isRowStyleInOuterDiv();    
 403  
     /** Partial block class */
 404  
     public abstract String getPartialBlockClass();
 405  
     /** For invoking linkToggleListener, if it exists */
 406  
     public abstract ListenerInvoker getListenerInvoker();
 407  
     /** Listener to invoke */
 408  
     public abstract IActionListener getLinkListener();
 409  
     /** Delayed loading parameter */
 410  
     public abstract boolean isDelayedLoad();    
 411  
     /** Delayed loading display element id */
 412  
     public abstract String getLoadElement();
 413  
 }