You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Igor Drobiazko <ig...@gmail.com> on 2011/06/16 22:05:45 UTC

Fwd: svn commit: r1135351 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/javascript/ main/java/org/apache/tapestry5/tree/ main/resources/org/apache

Unfortunately this commit made Tapestry to depend on Java 1.6. Class
java.util.Deque
is available starting from Java 1.6.

---------- Forwarded message ----------
From: <hl...@apache.org>
Date: Tue, Jun 14, 2011 at 2:40 AM
Subject: svn commit: r1135351 - in
/tapestry/tapestry5/trunk/tapestry-core/src:
main/java/org/apache/tapestry5/corelib/components/
main/java/org/apache/tapestry5/internal/services/javascript/
main/java/org/apache/tapestry5/tree/ main/resources/org/apache/tapest...
To: commits@tapestry.apache.org


Author: hlship
Date: Tue Jun 14 00:40:55 2011
New Revision: 1135351

URL: http://svn.apache.org/viewvc?rev=1135351&view=rev
Log:
Copy TapX Tree component into tapestry-core

Added:

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css

 tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js

 tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java

 tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java

 tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
Modified:

 tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java

 tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,294 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.corelib.components;
+
+import java.util.List;
+
+import org.apache.tapestry5.BindingConstants;
+import org.apache.tapestry5.Block;
+import org.apache.tapestry5.ComponentResources;
+import org.apache.tapestry5.Link;
+import org.apache.tapestry5.MarkupWriter;
+import org.apache.tapestry5.annotations.Environmental;
+import org.apache.tapestry5.annotations.Parameter;
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.dom.Element;
+import org.apache.tapestry5.func.F;
+import org.apache.tapestry5.func.Flow;
+import org.apache.tapestry5.func.Worker;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.json.JSONObject;
+import org.apache.tapestry5.runtime.RenderCommand;
+import org.apache.tapestry5.runtime.RenderQueue;
+import org.apache.tapestry5.services.javascript.JavaScriptSupport;
+import org.apache.tapestry5.tree.DefaultTreeExpansionModel;
+import org.apache.tapestry5.tree.TreeExpansionModel;
+import org.apache.tapestry5.tree.TreeModel;
+import org.apache.tapestry5.tree.TreeNode;
+
+/**
+ * A component used to render a recursive tree structure, with
expandable/collapsable nodes. The data that is displayed
+ * by the component is provided as a {@link TreeModel}. A secondary model,
the {@link TreeExpansionModel}, is used
+ * to track which nodes have been expanded. The Tree component uses special
tricks to support recursive rendering
+ * of the Tree as necessary.
+ *
+ * @since 5.3.0
+ */
+@SuppressWarnings(
+{ "rawtypes", "unchecked", "unused" })
+public class Tree
+{
+    /**
+     * The model that drives the tree, determining top level nodes and
making revealing the overall structure of the
+     * tree.
+     */
+    @Parameter(required = true, autoconnect = true)
+    private TreeModel model;
+
+    /**
+     * Allows the container to specify additional CSS class names for the
outer DIV element. The outer DIV
+     * always has the class name "t-tree-container"; the additional class
names are typically used to apply
+     * a specific size and width to the component.
+     */
+    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL)
+    private String className;
+
+    /**
+     * Optional parameter used to inform the container about what TreeNode
is currently rendering; this
+     * is primarily used when the label parameter is bound.
+     */
+    @Property
+    @Parameter
+    private TreeNode node;
+
+    /**
+     * Used to control the Tree's expansion model. By default, a persistent
field inside the Tree
+     * component stores a {@link DefaultTreeExpansionModel}. This parameter
may be bound when more
+     * control over the implementation of the expansion model, or how it is
stored, is
+     * required.
+     */
+    @Parameter(allowNull = false, value = "defaultTreeExpansionModel")
+    private TreeExpansionModel expansionModel;
+
+    /**
+     * Optional parameter used to inform the container about the value of
the currently rendering TreeNode; this
+     * is often preferable to the TreeNode, and like the node parameter, is
primarily used when the label parameter
+     * it bound.
+     */
+    @Parameter
+    private Object value;
+
+    /**
+     * A renderable (usually a {@link Block}) that can render the label for
a tree node.
+     * This will be invoked after the {@link #value} parameter has been
updated.
+     */
+    @Property
+    @Parameter(value = "block:defaultRenderTreeNodeLabel")
+    private RenderCommand label;
+
+    @Environmental
+    private JavaScriptSupport jss;
+
+    @Inject
+    private ComponentResources resources;
+
+    @Persist
+    private TreeExpansionModel defaultTreeExpansionModel;
+
+    private static RenderCommand RENDER_CLOSE_TAG = new RenderCommand()
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            writer.end();
+        };
+    };
+
+    private static RenderCommand RENDER_LABEL_SPAN = new RenderCommand()
+    {
+        public void render(MarkupWriter writer, RenderQueue queue)
+        {
+            writer.element("span", "class", "t-tree-label");
+        };
+    };
+
+    /**
+     * Renders a single node (which may be the last within its containing
node).
+     * This is a mix of immediate rendering, and queuing up various Blocks
and Render commands
+     * to do the rest. May recursively render child nodes of the active
node.
+     *
+     * @param node
+     *            to render
+     * @param isLast
+     *            if true, add "t-last" attribute to the LI element
+     * @return command to render the node
+     */
+    private RenderCommand toRenderCommand(final TreeNode node, final
boolean isLast)
+    {
+        return new RenderCommand()
+        {
+            public void render(MarkupWriter writer, RenderQueue queue)
+            {
+                // Inform the component's container about what value is
being rendered
+                // (this may be necessary to generate the correct label for
the node).
+                Tree.this.node = node;
+
+                value = node.getValue();
+
+                writer.element("li");
+
+                if (isLast)
+                    writer.attributes("class", "t-last");
+
+                Element e = writer.element("span", "class", "t-tree-icon");
+
+                if (node.isLeaf())
+                    e.addClassName("t-leaf-node");
+                else if (!node.getHasChildren())
+                    e.addClassName("t-empty-node");
+
+                boolean hasChildren = !node.isLeaf() &&
node.getHasChildren();
+                boolean expanded = hasChildren &&
expansionModel.isExpanded(node);
+
+                if (hasChildren)
+                {
+                    String clientId = jss.allocateClientId(resources);
+
+                    e.attribute("id", clientId);
+
+                    Link expandChildren =
resources.createEventLink("expandChildren", node.getId());
+                    Link markExpanded =
resources.createEventLink("markExpanded", node.getId());
+                    Link markCollapsed =
resources.createEventLink("markCollapsed", node.getId());
+
+                    JSONObject spec = new JSONObject("clientId", clientId,
+
+                    "expandChildrenURL", expandChildren.toString(),
+
+                    "markExpandedURL", markExpanded.toString(),
+
+                    "markCollapsedURL", markCollapsed.toString());
+
+                    if (expanded)
+                        spec.put("expanded", true);
+
+                    jss.addInitializerCall("treeNode", spec);
+                }
+
+                writer.end(); // span.tx-tree-icon
+
+                // From here on in, we're pushing things onto the queue.
Remember that
+                // execution order is reversed from order commands are
pushed.
+
+                queue.push(RENDER_CLOSE_TAG); // li
+
+                if (expanded)
+                {
+                    queue.push(new RenderNodes(node.getChildren()));
+                }
+
+                queue.push(RENDER_CLOSE_TAG);
+                queue.push(label);
+                queue.push(RENDER_LABEL_SPAN);
+
+            }
+        };
+    }
+
+    /** Renders an &lt;ul&gt; element and renders each node recusively
inside the element. */
+    private class RenderNodes implements RenderCommand
+    {
+        private final Flow<TreeNode> nodes;
+
+        public RenderNodes(List<TreeNode> nodes)
+        {
+            assert !nodes.isEmpty();
+
+            this.nodes = F.flow(nodes).reverse();
+        }
+
+        public void render(MarkupWriter writer, final RenderQueue queue)
+        {
+            writer.element("ul");
+            queue.push(RENDER_CLOSE_TAG);
+
+            queue.push(toRenderCommand(nodes.first(), true));
+
+            nodes.rest().each(new Worker<TreeNode>()
+            {
+                public void work(TreeNode element)
+                {
+                    queue.push(toRenderCommand(element, false));
+                }
+            });
+        }
+    }
+
+    public String getContainerClass()
+    {
+        return className == null ? "t-tree-container" : "t-tree-container "
+ className;
+    }
+
+    Object onExpandChildren(String nodeId)
+    {
+        TreeNode container = model.getById(nodeId);
+
+        expansionModel.markExpanded(container);
+
+        return new RenderNodes(container.getChildren());
+    }
+
+    Object onMarkExpanded(String nodeId)
+    {
+        expansionModel.markExpanded(model.getById(nodeId));
+
+        return new JSONObject();
+    }
+
+    Object onMarkCollapsed(String nodeId)
+    {
+        expansionModel.markCollapsed(model.getById(nodeId));
+
+        return new JSONObject();
+    }
+
+    public TreeExpansionModel getDefaultTreeExpansionModel()
+    {
+        if (defaultTreeExpansionModel == null)
+            defaultTreeExpansionModel = new DefaultTreeExpansionModel();
+
+        return defaultTreeExpansionModel;
+    }
+
+    /**
+     * Returns the actual {@link TreeExpansionModel} in use for this Tree
component,
+     * as per the expansionModel parameter. This is often, but not always,
the same
+     * as {@link #getDefaultTreeExpansionModel()}.
+     */
+    public TreeExpansionModel getExpansionModel()
+    {
+        return expansionModel;
+    }
+
+    public Object getRenderRootNodes()
+    {
+        return new RenderNodes(model.getRootNodes());
+    }
+
+    /** Clears the tree's {@link TreeExpansionModel}. */
+    public void clearExpansions()
+    {
+        expansionModel.clear();
+    }
+}

Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java?rev=1135351&r1=1135350&r2=1135351&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
Tue Jun 14 00:40:55 2011
@@ -69,17 +69,20 @@ public class CoreJavaScriptStack impleme
            ROOT + "/t5-core.js",

            ROOT + "/t5-arrays.js",
-
+
            ROOT + "/t5-init.js",
-
+
            ROOT + "/t5-pubsub.js",

-            ROOT + "/tapestry.js" };
+            ROOT + "/tapestry.js",
+
+            ROOT + "/tree.js" };

    // Because of changes to the logic of how stylesheets get incorporated,
the default stylesheet
    // was removed, the logic for it is now in
TapestryModule.contributeMarkupRenderer().

-    private static final String[] CORE_STYLESHEET = new String[0];
+    private static final String[] CORE_STYLESHEET = new String[]
+    { ROOT + "/tree.css" };

    public CoreJavaScriptStack(@Symbol(SymbolConstants.PRODUCTION_MODE)
    boolean productionMode,

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,64 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import java.util.Set;
+
+import org.apache.tapestry5.BaseOptimizedSessionPersistedObject;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+
+/**
+ * Manages a Set of String {@link TreeNode} ids.
+ *
+ * @param <T>
+ * @since 5.3.0
+ * @see TreeModel
+ */
+public class DefaultTreeExpansionModel<T> extends
BaseOptimizedSessionPersistedObject implements TreeExpansionModel<T>
+{
+    private final Set<String> expandedIds = CollectionFactory.newSet();
+
+    public boolean isExpanded(TreeNode<T> node)
+    {
+        assert node != null;
+
+        return expandedIds.contains(node.getId());
+    }
+
+    public void markExpanded(TreeNode<T> node)
+    {
+        assert node != null;
+
+        if (expandedIds.add(node.getId()))
+            markDirty();
+    }
+
+    public void markCollapsed(TreeNode<T> node)
+    {
+        assert node != null;
+
+        if (expandedIds.remove(node.getId()))
+            markDirty();
+    }
+
+    public void clear()
+    {
+        if (!expandedIds.isEmpty())
+        {
+            expandedIds.clear();
+            markDirty();
+        }
+    }
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,189 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import java.util.Collections;
+import java.util.Deque;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.func.F;
+import org.apache.tapestry5.func.Mapper;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+
+/**
+ * A default implementation of TreeModel that starts with a {@link
ValueEncoder} (for the element to string conversion),
+ * a {@link TreeModelAdapter}, and a list of root nodes.
+ * <p>
+ * This implementation is <em>not</em> thread safe.
+ *
+ * @param <T>
+ * @since 5.3.0
+ */
+public class DefaultTreeModel<T> implements TreeModel<T>
+{
+    private final ValueEncoder<T> encoder;
+
+    private final TreeModelAdapter<T> adapter;
+
+    private final List<TreeNode<T>> roots;
+
+    private final Map<String, TreeNode<T>> cache =
CollectionFactory.newMap();
+
+    private final Mapper<T, TreeNode<T>> toTreeNode = new Mapper<T,
TreeNode<T>>()
+    {
+        public TreeNode<T> map(T value)
+        {
+            return new DefaultTreeNode(value);
+        };
+    };
+
+    private class DefaultTreeNode implements TreeNode<T>
+    {
+        private final T value;
+
+        private List<TreeNode<T>> children;
+
+        DefaultTreeNode(T value)
+        {
+            this.value = value;
+        }
+
+        public String getId()
+        {
+            return encoder.toClient(value);
+        }
+
+        public T getValue()
+        {
+            return value;
+        }
+
+        public boolean isLeaf()
+        {
+            return adapter.isLeaf(value);
+        }
+
+        public boolean getHasChildren()
+        {
+            return adapter.hasChildren(value);
+        }
+
+        public List<TreeNode<T>> getChildren()
+        {
+            if (children == null)
+                children =
F.flow(adapter.getChildren(value)).map(toTreeNode).toList();
+
+            return children;
+        }
+
+        public String getLabel()
+        {
+            return adapter.getLabel(value);
+        }
+
+    }
+
+    /**
+     * Creates a new model starting from a single root element.
+     *
+     * @param encoder
+     * @param adapter
+     * @param root
+     */
+    public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T>
adapter, T root)
+    {
+        this(encoder, adapter, Collections.singletonList(root));
+    }
+
+    /**
+     * Standard constructor.
+     *
+     * @param encoder
+     *            used to convert values to strings and vice-versa
+     * @param adapter
+     *            adapts elements to the tree
+     * @param roots
+     *            defines the root nodes of the model
+     */
+    public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T>
adapter, List<T> roots)
+    {
+        assert encoder != null;
+        assert adapter != null;
+        assert roots != null;
+        assert !roots.isEmpty();
+
+        this.encoder = encoder;
+        this.adapter = adapter;
+        this.roots = F.flow(roots).map(toTreeNode).toList();
+    }
+
+    public List<TreeNode<T>> getRootNodes()
+    {
+        return roots;
+    }
+
+    public TreeNode<T> getById(String id)
+    {
+        assert id != null;
+
+        TreeNode<T> result = findById(id);
+
+        if (result == null)
+            throw new IllegalArgumentException(String.format("Could not
locate TreeNode '%s'.", id));
+
+        return result;
+    }
+
+    private TreeNode<T> findById(String id)
+    {
+        TreeNode<T> result = cache.get(id);
+
+        if (result != null)
+            return result;
+
+        Deque<TreeNode<T>> queue = new LinkedList<TreeNode<T>>(roots);
+
+        while (!queue.isEmpty())
+        {
+            TreeNode<T> node = queue.removeFirst();
+
+            String nodeId = node.getId();
+
+            cache.put(nodeId, node);
+
+            if (nodeId.equals(id))
+                return node;
+
+            if (!node.isLeaf() && node.getHasChildren())
+            {
+                for (TreeNode<T> child : node.getChildren())
+                {
+                    queue.addFirst(child);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    public TreeNode<T> find(T element)
+    {
+        return findById(encoder.toClient(element));
+    }
+
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,47 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import org.apache.tapestry5.corelib.components.Tree;
+
+/**
+ * Tracks which nodes of a {@link TreeModel} are currently expanded. The
{@linkplain DefaultTreeExpansionModel default
+ * implementation} simply stores a set of {@linkplain TreeNode#getId()
unique node
+ * ids} to identify expanded nodes. The expansion model is updated whenever
folders are expanded or
+ * collapsed on the client side.
+ *
+ * @since 5.3.0
+ * @see Tree
+ */
+public interface TreeExpansionModel<T>
+{
+    /**
+     * Returns true if the node has been previously {@linkplain
#markExpanded(TreeNode) expanded}.
+     *
+     * @param node
+     *            node to check for expansion
+     * @return
+     */
+    boolean isExpanded(TreeNode<T> node);
+
+    /** Marks the node as expanded. */
+    void markExpanded(TreeNode<T> node);
+
+    /** Marks the node as collapsed (not expanded). */
+    void markCollapsed(TreeNode<T> node);
+
+    /** Marks all nodes as collapsed. */
+    void clear();
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,59 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import java.util.List;
+
+import javax.swing.tree.TreeSelectionModel;
+
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.corelib.components.Tree;
+
+/**
+ * A model for tree-oriented data used by the {@link Tree} component. The
default implemention, {@link DefaultTreeModel}
+ * uses a {@link ValueEncoder} and a {@link TreeModelAdapter} to supply the
+ * underlying information.
+ *
+ * @param <T>
+ *            type of data in the tree
+ * @since 5.3.0
+ * @see TreeSelectionModel
+ */
+public interface TreeModel<T>
+{
+    /**
+     * Returns the node or nodes that are the top level of the tree.
+     */
+    List<TreeNode<T>> getRootNodes();
+
+    /**
+     * Locates a node in the tree by its unique id.
+     *
+     * @throws IllegalArgumentException
+     *             if no such node exists
+     * @see TreeNode#getId()
+     */
+    TreeNode<T> getById(String id);
+
+    /**
+     * Recursively searches from the root nodes to find the tree node that
matches
+     * the provided element.
+     *
+     * @param element
+     *            to search for
+     * @return matching node, or null if not found
+     */
+    TreeNode<T> find(T element);
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,53 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import java.util.List;
+
+/**
+ * Used with {@link DefaultTreeModel} to define how to extract labels and
child nodes from a value.
+ *
+ * @since 5.3.0
+ */
+public interface TreeModelAdapter<T>
+{
+    /**
+     * Determines if the value is a leaf or a (potential) container of
children.
+     *
+     * @see TreeNode#isLeaf()
+     */
+    boolean isLeaf(T value);
+
+    /**
+     * Returns true if the value has children (only invoked for non-leaf
values).
+     *
+     * @see TreeNode#getHasChildren()
+     */
+    boolean hasChildren(T value);
+
+    /**
+     * Returns the children, in the order they should be presented to the
client.
+     *
+     * @see TreeNode#getChildren()
+     */
+    List<T> getChildren(T value);
+
+    /**
+     * Returns a text label for the value, which may be presented to the
client.
+     *
+     * @see TreeNode#getLabel()
+     */
+    String getLabel(T value);
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,72 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.tree;
+
+import java.util.List;
+
+/**
+ * A node within a {@link TreeModel}. In a {@link DefaultTreeModel}, most
of the node's information
+ * comes via the {@link TreeModelAdapter}.
+ *
+ * @param <T>
+ *            type of node
+ * @since 5.3.0
+ */
+public interface TreeNode<T>
+{
+    /**
+     * Returns a string Id for the node that uniquely identifies it.
+     *
+     * @return unique string identifying the node
+     * @see TreeModel#getById(String)
+     */
+    String getId();
+
+    /** Returns the value represented by this node. */
+    T getValue();
+
+    /**
+     * If true, then this node is a leaf node, which never has children
(i.e., a file). If false, the node
+     * may have children (i.e., a folder).
+     *
+     * @return true for leaf nodes, false for folder nodes
+     * @see TreeModelAdapter#isLeaf(Object)
+     */
+    boolean isLeaf();
+
+    /**
+     * Returns true if this non-leaf node has child nodes. This will not be
invoked for leaf nodes.
+     *
+     * @see TreeModelAdapter#hasChildren(Object)
+     */
+    boolean getHasChildren();
+
+    /**
+     * Returns the actual children of this non-leaf node, as additional
nodes.
+     *
+     * @see TreeModelAdapter#getChildren(Object)
+     */
+    List<TreeNode<T>> getChildren();
+
+    // TODO: Some way to influence the rendered output (i.e., to display
different icons based on
+    // file type).
+
+    /**
+     * Returns a textual label for the node. Not all UIs will make use of
the label, but default UIs will.
+     *
+     * @see TreeModelAdapter#getLabel(Object)
+     */
+    public String getLabel();
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,11 @@
+<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
+
+  <div class="${containerClass}">
+    <t:delegate to="renderRootNodes"/>
+  </div>
+
+  <t:block id="defaultRenderTreeNodeLabel">
+    ${node.label}
+  </t:block>
+
+</t:container>
\ No newline at end of file

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png?rev=1135351&view=auto
==============================================================================
Files
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
(added) and
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
Tue Jun 14 00:40:55 2011 differ

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png?rev=1135351&view=auto
==============================================================================
Files
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
(added) and
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
Tue Jun 14 00:40:55 2011 differ

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png?rev=1135351&view=auto
==============================================================================
Files
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
(added) and
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
Tue Jun 14 00:40:55 2011 differ

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png?rev=1135351&view=auto
==============================================================================
Files
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
(added) and
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
Tue Jun 14 00:40:55 2011 differ

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,84 @@
+
+DIV.t-tree-container
+{
+  padding: 0;
+  margin: 0;
+}
+
+DIV.t-tree-container UL
+{
+  list-style-type: none;
+  background-image: url(tree-vpipe.png);
+  background-repeat: repeat-y;
+  margin: 0 0 0 12px;
+  padding: 0;
+}
+
+DIV.t-tree-container UL UL
+{
+  /* Line up the nested list's vertical bar under the element's folder
icon. */
+  margin: 0 0 0 24px;
+}
+
+
+DIV.t-tree-container LI {
+  margin: 0;
+  padding: 0 0 0 16px;
+  background-image: url(tree-branch.png);
+  background-repeat: no-repeat;
+  line-height: 1.5;
+}
+
+DIV.t-tree-container LI.t-last
+{
+    background-color: white;
+    background-image: url(tree-branchend.png);
+}
+
+/* Assume its a collapsed, but expandable, tree node. Later CSS rules
overwrite this. */
+
+SPAN.t-tree-icon {
+  display: inline-block;
+  width: 32px;
+  height: 16px;
+  cursor: pointer;
+  background-image: url(tree-sprites.png);
+  background-position: 0px 0px;
+}
+
+SPAN.t-tree-icon.t-leaf-node {
+  cursor: default;
+  background-position: -32px -16px;
+}
+
+SPAN.t-tree-icon.t-empty-node {
+  cursor: default;
+  background-position: -32px 0px !important;
+}
+
+SPAN.t-tree-expanded {
+  background-position: 0px -16px;
+}
+
+SPAN.t-ajax-wait {
+  width: 16px;
+  height: 16px;
+  display: inline-block;
+  background-image: url(ajax-loader.gif);
+}
+
+SPAN.t-tree-icon.t-expand {
+  width: 16px;
+  height: 16px;
+  background-image: url(expand.png);
+  display: inline-block;
+  cursor: pointer;
+}
+
+SPAN.t-tree-icon.t-collapse {
+  width: 16px;
+  height: 16px;
+  background-image: url(collapse.png);
+  display: inline-block;
+  cursor: pointer;
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,138 @@
+T5.extend(T5, {
+       Tree : {
+
+               /**
+                * Approximate time per pixel for the hide and reveal
animations. The
+                * idea is to have small (few children) and large (many
childen)
+                * animations operate at the same visible rate, even though
they will
+                * take different amounts of time.
+                */
+               ANIMATION_RATE : .005,
+
+               /**
+                * Maximum animation time, in seconds. This is necessary for
very large
+                * animations, otherwise its looks visually odd to see the
child tree
+                * nodes whip down the screen.
+                */
+               MAX_ANIMATION_DURATION : .5,
+
+               /** Type of Scriptaculous effect to hide/show child nodes.
*/
+               TOGGLE_TYPE : 'blind',
+
+               /**
+                * Name of Scriptaculous effects queue to ensure that
animations do not
+                * overwrite each other.
+                */
+               QUEUE_NAME : 't-tree-updates'
+       }
+});
+
+T5.extendInitializer(function() {
+
+       var cfg = T5.Tree;
+
+       function doAnimate(element) {
+               var sublist = $(element).up('li').down("ul");
+
+               var dim = sublist.getDimensions();
+
+               var duration = Math.min(dim.height * cfg.ANIMATION_RATE,
+                               cfg.MAX_ANIMATION_DURATION)
+
+               new Effect.toggle(sublist, cfg.TOGGLE_TYPE, {
+                       duration : duration,
+                       queue : {
+                               position : 'end',
+                               scope : cfg.QUEUE_NAME
+                       }
+               });
+       }
+
+       function animateRevealChildren(element) {
+               $(element).addClassName("t-tree-expanded");
+
+               doAnimate(element);
+       }
+
+       function animateHideChildren(element) {
+               $(element).removeClassName("t-tree-expanded");
+
+               doAnimate(element);
+       }
+
+       function initializer(spec) {
+               var loaded = spec.expanded;
+               var expanded = spec.expanded;
+               if (expanded) {
+                       $(spec.clientId).addClassName("t-tree-expanded")
+               }
+               var loading = false;
+
+               function successHandler(reply) {
+                       // Remove the Ajax load indicator
+                       $(spec.clientId).update("");
+                       $(spec.clientId).removeClassName("t-empty-node");
+
+                       var response = reply.responseJSON;
+
+                       Tapestry.loadScriptsInReply(response, function() {
+                               var element = $(spec.clientId).up("li");
+                               var label =
element.down("span.t-tree-label");
+
+                               label.insert({
+                                       after : response.content
+                               });
+
+                               // Hide the new sublist so that we can
animate revealing it.
+                               element.down("ul").hide();
+
+                               animateRevealChildren(spec.clientId);
+
+                               loading = false;
+                               loaded = true;
+                               expanded = true;
+                       });
+
+               }
+
+               function doLoad() {
+                       if (loading)
+                               return;
+
+                       loading = true;
+
+                       $(spec.clientId).addClassName("t-empty-node");
+                       $(spec.clientId).update("<span
class='t-ajax-wait'/>");
+
+                       Tapestry.ajaxRequest(spec.expandChildrenURL,
successHandler);
+               }
+
+               $(spec.clientId).observe("click", function(event) {
+                       event.stop();
+
+                       if (!loaded) {
+
+                               doLoad();
+
+                               return;
+                       }
+
+                       // Children have been loaded, just a matter of
toggling
+                       // between showing or hiding the children.
+
+                       var f = expanded ? animateHideChildren :
animateRevealChildren;
+
+                       f.call(null, spec.clientId);
+
+                       var url = expanded ? spec.markCollapsedURL :
spec.markExpandedURL;
+
+                       Tapestry.ajaxRequest(url, {});
+
+                       expanded = !expanded;
+               });
+       }
+
+       return {
+               treeNode : initializer
+       };
+});

Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,75 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.app1;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.func.F;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.tree.DefaultTreeModel;
+import org.apache.tapestry5.tree.TreeModelAdapter;
+
+public class FileSystemTreeModel extends DefaultTreeModel<File>
+{
+    private static final ValueEncoder<File> ENCODER = new
ValueEncoder<File>()
+    {
+
+        public String toClient(File value)
+        {
+            return value.getPath();
+        }
+
+        public File toValue(String clientValue)
+        {
+            return new File(clientValue);
+        }
+    };
+
+    private static final TreeModelAdapter<File> ADAPTER = new
TreeModelAdapter<File>()
+    {
+
+        public boolean isLeaf(File value)
+        {
+            return value.isFile();
+        }
+
+        public boolean hasChildren(File value)
+        {
+            return value.list().length > 0;
+        }
+
+        public List<File> getChildren(File value)
+        {
+            return CollectionFactory.newList(value.listFiles());
+        }
+
+        public String getLabel(File value)
+        {
+            return value.getName();
+        }
+    };
+
+    public FileSystemTreeModel()
+    {
+        this(".");
+    }
+
+    public FileSystemTreeModel(String rootDir)
+    {
+        super(ENCODER, ADAPTER, F.flow(new
File(rootDir).listFiles()).toList());
+    }
+}

Modified:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=1135351&r1=1135350&r2=1135351&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
Tue Jun 14 00:40:55 2011
@@ -57,6 +57,8 @@ public class Index
    private static final List<Item> ITEMS = CollectionFactory
            .newList(

+                    new Item("TreeDemo", "Tree Component Demo", "Demo of
Tree Component"),
+
                    new Item("InvalidExpressionInDynamicTemplate", "Invalid
Dynamic Expression",
                            "Invalid expression in a Dynamic Template"),


Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,38 @@
+// Copyright 2011 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry5.integration.app1.pages;
+
+import java.io.File;
+
+import org.apache.tapestry5.annotations.InjectComponent;
+import org.apache.tapestry5.corelib.components.Tree;
+import org.apache.tapestry5.integration.app1.FileSystemTreeModel;
+import org.apache.tapestry5.tree.TreeModel;
+
+public class TreeDemo
+{
+    @InjectComponent
+    private Tree fs;
+
+    public TreeModel<File> getFileSystemTreeModel()
+    {
+        return new FileSystemTreeModel();
+    }
+
+    void onActionFromClear()
+    {
+        fs.clearExpansions();
+    }
+}

Added:
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml?rev=1135351&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
Tue Jun 14 00:40:55 2011
@@ -0,0 +1,15 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
xmlns:p="tapestry:parameter">
+
+  <h1>Tree Demo</h1>
+
+  <t:tree t:id="fs" model="fileSystemTreeModel"/>
+
+  <p>
+    [
+    <t:pagelink page="treeDemo">Redraw</t:pagelink>
+    ]
+    [
+    <t:actionlink t:id="clear">clear expansions</t:actionlink>
+    ]
+  </p>
+</t:border>
\ No newline at end of file





-- 
Best regards,

Igor Drobiazko
http://tapestry5.de

Re: svn commit: r1135351 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/javascript/ main/java/org/apache/tapestry5/tree/ main/resources/org/apache

Posted by Kalle Korhonen <ka...@gmail.com>.
Agree it shouldn't - it's easy to document and fix in a follow-up
release. I started running against several tynamo modules but since
the dependencies have changed enough, it'll take some tweaking before
I have a better picture.

Kalle


On Thu, Jun 16, 2011 at 1:52 PM, Howard Lewis Ship <hl...@gmail.com> wrote:
> Good catch, needs to be fixed, but should it hold up this first alpha
> release?
> On Jun 16, 2011 1:12 PM, "Josh Canfield" <jo...@gmail.com> wrote:
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org


Re: svn commit: r1135351 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/javascript/ main/java/org/apache/tapestry5/tree/ main/resources/org/apache

Posted by Howard Lewis Ship <hl...@gmail.com>.
Good catch, needs to be fixed, but should it hold up this first alpha
release?
On Jun 16, 2011 1:12 PM, "Josh Canfield" <jo...@gmail.com> wrote:

Re: svn commit: r1135351 - in /tapestry/tapestry5/trunk/tapestry-core/src: main/java/org/apache/tapestry5/corelib/components/ main/java/org/apache/tapestry5/internal/services/javascript/ main/java/org/apache/tapestry5/tree/ main/resources/org/apache

Posted by Josh Canfield <jo...@gmail.com>.
> Unfortunately this commit made Tapestry to depend on Java 1.6. Class
> java.util.Deque
> is available starting from Java 1.6.

Another good reason to see about getting hudson to compile and run
unit tests in other versions/implementations of the JVM! :)

On Thu, Jun 16, 2011 at 1:05 PM, Igor Drobiazko
<ig...@gmail.com> wrote:
> Unfortunately this commit made Tapestry to depend on Java 1.6. Class
> java.util.Deque
> is available starting from Java 1.6.
>
> ---------- Forwarded message ----------
> From: <hl...@apache.org>
> Date: Tue, Jun 14, 2011 at 2:40 AM
> Subject: svn commit: r1135351 - in
> /tapestry/tapestry5/trunk/tapestry-core/src:
> main/java/org/apache/tapestry5/corelib/components/
> main/java/org/apache/tapestry5/internal/services/javascript/
> main/java/org/apache/tapestry5/tree/ main/resources/org/apache/tapest...
> To: commits@tapestry.apache.org
>
>
> Author: hlship
> Date: Tue Jun 14 00:40:55 2011
> New Revision: 1135351
>
> URL: http://svn.apache.org/viewvc?rev=1135351&view=rev
> Log:
> Copy TapX Tree component into tapestry-core
>
> Added:
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
>
>  tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
> Modified:
>
>  tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
>
>  tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/Tree.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,294 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.corelib.components;
> +
> +import java.util.List;
> +
> +import org.apache.tapestry5.BindingConstants;
> +import org.apache.tapestry5.Block;
> +import org.apache.tapestry5.ComponentResources;
> +import org.apache.tapestry5.Link;
> +import org.apache.tapestry5.MarkupWriter;
> +import org.apache.tapestry5.annotations.Environmental;
> +import org.apache.tapestry5.annotations.Parameter;
> +import org.apache.tapestry5.annotations.Persist;
> +import org.apache.tapestry5.annotations.Property;
> +import org.apache.tapestry5.dom.Element;
> +import org.apache.tapestry5.func.F;
> +import org.apache.tapestry5.func.Flow;
> +import org.apache.tapestry5.func.Worker;
> +import org.apache.tapestry5.ioc.annotations.Inject;
> +import org.apache.tapestry5.json.JSONObject;
> +import org.apache.tapestry5.runtime.RenderCommand;
> +import org.apache.tapestry5.runtime.RenderQueue;
> +import org.apache.tapestry5.services.javascript.JavaScriptSupport;
> +import org.apache.tapestry5.tree.DefaultTreeExpansionModel;
> +import org.apache.tapestry5.tree.TreeExpansionModel;
> +import org.apache.tapestry5.tree.TreeModel;
> +import org.apache.tapestry5.tree.TreeNode;
> +
> +/**
> + * A component used to render a recursive tree structure, with
> expandable/collapsable nodes. The data that is displayed
> + * by the component is provided as a {@link TreeModel}. A secondary model,
> the {@link TreeExpansionModel}, is used
> + * to track which nodes have been expanded. The Tree component uses special
> tricks to support recursive rendering
> + * of the Tree as necessary.
> + *
> + * @since 5.3.0
> + */
> +@SuppressWarnings(
> +{ "rawtypes", "unchecked", "unused" })
> +public class Tree
> +{
> +    /**
> +     * The model that drives the tree, determining top level nodes and
> making revealing the overall structure of the
> +     * tree.
> +     */
> +    @Parameter(required = true, autoconnect = true)
> +    private TreeModel model;
> +
> +    /**
> +     * Allows the container to specify additional CSS class names for the
> outer DIV element. The outer DIV
> +     * always has the class name "t-tree-container"; the additional class
> names are typically used to apply
> +     * a specific size and width to the component.
> +     */
> +    @Parameter(name = "class", defaultPrefix = BindingConstants.LITERAL)
> +    private String className;
> +
> +    /**
> +     * Optional parameter used to inform the container about what TreeNode
> is currently rendering; this
> +     * is primarily used when the label parameter is bound.
> +     */
> +    @Property
> +    @Parameter
> +    private TreeNode node;
> +
> +    /**
> +     * Used to control the Tree's expansion model. By default, a persistent
> field inside the Tree
> +     * component stores a {@link DefaultTreeExpansionModel}. This parameter
> may be bound when more
> +     * control over the implementation of the expansion model, or how it is
> stored, is
> +     * required.
> +     */
> +    @Parameter(allowNull = false, value = "defaultTreeExpansionModel")
> +    private TreeExpansionModel expansionModel;
> +
> +    /**
> +     * Optional parameter used to inform the container about the value of
> the currently rendering TreeNode; this
> +     * is often preferable to the TreeNode, and like the node parameter, is
> primarily used when the label parameter
> +     * it bound.
> +     */
> +    @Parameter
> +    private Object value;
> +
> +    /**
> +     * A renderable (usually a {@link Block}) that can render the label for
> a tree node.
> +     * This will be invoked after the {@link #value} parameter has been
> updated.
> +     */
> +    @Property
> +    @Parameter(value = "block:defaultRenderTreeNodeLabel")
> +    private RenderCommand label;
> +
> +    @Environmental
> +    private JavaScriptSupport jss;
> +
> +    @Inject
> +    private ComponentResources resources;
> +
> +    @Persist
> +    private TreeExpansionModel defaultTreeExpansionModel;
> +
> +    private static RenderCommand RENDER_CLOSE_TAG = new RenderCommand()
> +    {
> +        public void render(MarkupWriter writer, RenderQueue queue)
> +        {
> +            writer.end();
> +        };
> +    };
> +
> +    private static RenderCommand RENDER_LABEL_SPAN = new RenderCommand()
> +    {
> +        public void render(MarkupWriter writer, RenderQueue queue)
> +        {
> +            writer.element("span", "class", "t-tree-label");
> +        };
> +    };
> +
> +    /**
> +     * Renders a single node (which may be the last within its containing
> node).
> +     * This is a mix of immediate rendering, and queuing up various Blocks
> and Render commands
> +     * to do the rest. May recursively render child nodes of the active
> node.
> +     *
> +     * @param node
> +     *            to render
> +     * @param isLast
> +     *            if true, add "t-last" attribute to the LI element
> +     * @return command to render the node
> +     */
> +    private RenderCommand toRenderCommand(final TreeNode node, final
> boolean isLast)
> +    {
> +        return new RenderCommand()
> +        {
> +            public void render(MarkupWriter writer, RenderQueue queue)
> +            {
> +                // Inform the component's container about what value is
> being rendered
> +                // (this may be necessary to generate the correct label for
> the node).
> +                Tree.this.node = node;
> +
> +                value = node.getValue();
> +
> +                writer.element("li");
> +
> +                if (isLast)
> +                    writer.attributes("class", "t-last");
> +
> +                Element e = writer.element("span", "class", "t-tree-icon");
> +
> +                if (node.isLeaf())
> +                    e.addClassName("t-leaf-node");
> +                else if (!node.getHasChildren())
> +                    e.addClassName("t-empty-node");
> +
> +                boolean hasChildren = !node.isLeaf() &&
> node.getHasChildren();
> +                boolean expanded = hasChildren &&
> expansionModel.isExpanded(node);
> +
> +                if (hasChildren)
> +                {
> +                    String clientId = jss.allocateClientId(resources);
> +
> +                    e.attribute("id", clientId);
> +
> +                    Link expandChildren =
> resources.createEventLink("expandChildren", node.getId());
> +                    Link markExpanded =
> resources.createEventLink("markExpanded", node.getId());
> +                    Link markCollapsed =
> resources.createEventLink("markCollapsed", node.getId());
> +
> +                    JSONObject spec = new JSONObject("clientId", clientId,
> +
> +                    "expandChildrenURL", expandChildren.toString(),
> +
> +                    "markExpandedURL", markExpanded.toString(),
> +
> +                    "markCollapsedURL", markCollapsed.toString());
> +
> +                    if (expanded)
> +                        spec.put("expanded", true);
> +
> +                    jss.addInitializerCall("treeNode", spec);
> +                }
> +
> +                writer.end(); // span.tx-tree-icon
> +
> +                // From here on in, we're pushing things onto the queue.
> Remember that
> +                // execution order is reversed from order commands are
> pushed.
> +
> +                queue.push(RENDER_CLOSE_TAG); // li
> +
> +                if (expanded)
> +                {
> +                    queue.push(new RenderNodes(node.getChildren()));
> +                }
> +
> +                queue.push(RENDER_CLOSE_TAG);
> +                queue.push(label);
> +                queue.push(RENDER_LABEL_SPAN);
> +
> +            }
> +        };
> +    }
> +
> +    /** Renders an &lt;ul&gt; element and renders each node recusively
> inside the element. */
> +    private class RenderNodes implements RenderCommand
> +    {
> +        private final Flow<TreeNode> nodes;
> +
> +        public RenderNodes(List<TreeNode> nodes)
> +        {
> +            assert !nodes.isEmpty();
> +
> +            this.nodes = F.flow(nodes).reverse();
> +        }
> +
> +        public void render(MarkupWriter writer, final RenderQueue queue)
> +        {
> +            writer.element("ul");
> +            queue.push(RENDER_CLOSE_TAG);
> +
> +            queue.push(toRenderCommand(nodes.first(), true));
> +
> +            nodes.rest().each(new Worker<TreeNode>()
> +            {
> +                public void work(TreeNode element)
> +                {
> +                    queue.push(toRenderCommand(element, false));
> +                }
> +            });
> +        }
> +    }
> +
> +    public String getContainerClass()
> +    {
> +        return className == null ? "t-tree-container" : "t-tree-container "
> + className;
> +    }
> +
> +    Object onExpandChildren(String nodeId)
> +    {
> +        TreeNode container = model.getById(nodeId);
> +
> +        expansionModel.markExpanded(container);
> +
> +        return new RenderNodes(container.getChildren());
> +    }
> +
> +    Object onMarkExpanded(String nodeId)
> +    {
> +        expansionModel.markExpanded(model.getById(nodeId));
> +
> +        return new JSONObject();
> +    }
> +
> +    Object onMarkCollapsed(String nodeId)
> +    {
> +        expansionModel.markCollapsed(model.getById(nodeId));
> +
> +        return new JSONObject();
> +    }
> +
> +    public TreeExpansionModel getDefaultTreeExpansionModel()
> +    {
> +        if (defaultTreeExpansionModel == null)
> +            defaultTreeExpansionModel = new DefaultTreeExpansionModel();
> +
> +        return defaultTreeExpansionModel;
> +    }
> +
> +    /**
> +     * Returns the actual {@link TreeExpansionModel} in use for this Tree
> component,
> +     * as per the expansionModel parameter. This is often, but not always,
> the same
> +     * as {@link #getDefaultTreeExpansionModel()}.
> +     */
> +    public TreeExpansionModel getExpansionModel()
> +    {
> +        return expansionModel;
> +    }
> +
> +    public Object getRenderRootNodes()
> +    {
> +        return new RenderNodes(model.getRootNodes());
> +    }
> +
> +    /** Clears the tree's {@link TreeExpansionModel}. */
> +    public void clearExpansions()
> +    {
> +        expansionModel.clear();
> +    }
> +}
>
> Modified:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java?rev=1135351&r1=1135350&r2=1135351&view=diff
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
> (original)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/javascript/CoreJavaScriptStack.java
> Tue Jun 14 00:40:55 2011
> @@ -69,17 +69,20 @@ public class CoreJavaScriptStack impleme
>            ROOT + "/t5-core.js",
>
>            ROOT + "/t5-arrays.js",
> -
> +
>            ROOT + "/t5-init.js",
> -
> +
>            ROOT + "/t5-pubsub.js",
>
> -            ROOT + "/tapestry.js" };
> +            ROOT + "/tapestry.js",
> +
> +            ROOT + "/tree.js" };
>
>    // Because of changes to the logic of how stylesheets get incorporated,
> the default stylesheet
>    // was removed, the logic for it is now in
> TapestryModule.contributeMarkupRenderer().
>
> -    private static final String[] CORE_STYLESHEET = new String[0];
> +    private static final String[] CORE_STYLESHEET = new String[]
> +    { ROOT + "/tree.css" };
>
>    public CoreJavaScriptStack(@Symbol(SymbolConstants.PRODUCTION_MODE)
>    boolean productionMode,
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeExpansionModel.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,64 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import java.util.Set;
> +
> +import org.apache.tapestry5.BaseOptimizedSessionPersistedObject;
> +import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> +
> +/**
> + * Manages a Set of String {@link TreeNode} ids.
> + *
> + * @param <T>
> + * @since 5.3.0
> + * @see TreeModel
> + */
> +public class DefaultTreeExpansionModel<T> extends
> BaseOptimizedSessionPersistedObject implements TreeExpansionModel<T>
> +{
> +    private final Set<String> expandedIds = CollectionFactory.newSet();
> +
> +    public boolean isExpanded(TreeNode<T> node)
> +    {
> +        assert node != null;
> +
> +        return expandedIds.contains(node.getId());
> +    }
> +
> +    public void markExpanded(TreeNode<T> node)
> +    {
> +        assert node != null;
> +
> +        if (expandedIds.add(node.getId()))
> +            markDirty();
> +    }
> +
> +    public void markCollapsed(TreeNode<T> node)
> +    {
> +        assert node != null;
> +
> +        if (expandedIds.remove(node.getId()))
> +            markDirty();
> +    }
> +
> +    public void clear()
> +    {
> +        if (!expandedIds.isEmpty())
> +        {
> +            expandedIds.clear();
> +            markDirty();
> +        }
> +    }
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/DefaultTreeModel.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,189 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import java.util.Collections;
> +import java.util.Deque;
> +import java.util.LinkedList;
> +import java.util.List;
> +import java.util.Map;
> +
> +import org.apache.tapestry5.ValueEncoder;
> +import org.apache.tapestry5.func.F;
> +import org.apache.tapestry5.func.Mapper;
> +import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> +
> +/**
> + * A default implementation of TreeModel that starts with a {@link
> ValueEncoder} (for the element to string conversion),
> + * a {@link TreeModelAdapter}, and a list of root nodes.
> + * <p>
> + * This implementation is <em>not</em> thread safe.
> + *
> + * @param <T>
> + * @since 5.3.0
> + */
> +public class DefaultTreeModel<T> implements TreeModel<T>
> +{
> +    private final ValueEncoder<T> encoder;
> +
> +    private final TreeModelAdapter<T> adapter;
> +
> +    private final List<TreeNode<T>> roots;
> +
> +    private final Map<String, TreeNode<T>> cache =
> CollectionFactory.newMap();
> +
> +    private final Mapper<T, TreeNode<T>> toTreeNode = new Mapper<T,
> TreeNode<T>>()
> +    {
> +        public TreeNode<T> map(T value)
> +        {
> +            return new DefaultTreeNode(value);
> +        };
> +    };
> +
> +    private class DefaultTreeNode implements TreeNode<T>
> +    {
> +        private final T value;
> +
> +        private List<TreeNode<T>> children;
> +
> +        DefaultTreeNode(T value)
> +        {
> +            this.value = value;
> +        }
> +
> +        public String getId()
> +        {
> +            return encoder.toClient(value);
> +        }
> +
> +        public T getValue()
> +        {
> +            return value;
> +        }
> +
> +        public boolean isLeaf()
> +        {
> +            return adapter.isLeaf(value);
> +        }
> +
> +        public boolean getHasChildren()
> +        {
> +            return adapter.hasChildren(value);
> +        }
> +
> +        public List<TreeNode<T>> getChildren()
> +        {
> +            if (children == null)
> +                children =
> F.flow(adapter.getChildren(value)).map(toTreeNode).toList();
> +
> +            return children;
> +        }
> +
> +        public String getLabel()
> +        {
> +            return adapter.getLabel(value);
> +        }
> +
> +    }
> +
> +    /**
> +     * Creates a new model starting from a single root element.
> +     *
> +     * @param encoder
> +     * @param adapter
> +     * @param root
> +     */
> +    public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T>
> adapter, T root)
> +    {
> +        this(encoder, adapter, Collections.singletonList(root));
> +    }
> +
> +    /**
> +     * Standard constructor.
> +     *
> +     * @param encoder
> +     *            used to convert values to strings and vice-versa
> +     * @param adapter
> +     *            adapts elements to the tree
> +     * @param roots
> +     *            defines the root nodes of the model
> +     */
> +    public DefaultTreeModel(ValueEncoder<T> encoder, TreeModelAdapter<T>
> adapter, List<T> roots)
> +    {
> +        assert encoder != null;
> +        assert adapter != null;
> +        assert roots != null;
> +        assert !roots.isEmpty();
> +
> +        this.encoder = encoder;
> +        this.adapter = adapter;
> +        this.roots = F.flow(roots).map(toTreeNode).toList();
> +    }
> +
> +    public List<TreeNode<T>> getRootNodes()
> +    {
> +        return roots;
> +    }
> +
> +    public TreeNode<T> getById(String id)
> +    {
> +        assert id != null;
> +
> +        TreeNode<T> result = findById(id);
> +
> +        if (result == null)
> +            throw new IllegalArgumentException(String.format("Could not
> locate TreeNode '%s'.", id));
> +
> +        return result;
> +    }
> +
> +    private TreeNode<T> findById(String id)
> +    {
> +        TreeNode<T> result = cache.get(id);
> +
> +        if (result != null)
> +            return result;
> +
> +        Deque<TreeNode<T>> queue = new LinkedList<TreeNode<T>>(roots);
> +
> +        while (!queue.isEmpty())
> +        {
> +            TreeNode<T> node = queue.removeFirst();
> +
> +            String nodeId = node.getId();
> +
> +            cache.put(nodeId, node);
> +
> +            if (nodeId.equals(id))
> +                return node;
> +
> +            if (!node.isLeaf() && node.getHasChildren())
> +            {
> +                for (TreeNode<T> child : node.getChildren())
> +                {
> +                    queue.addFirst(child);
> +                }
> +            }
> +        }
> +
> +        return null;
> +    }
> +
> +    public TreeNode<T> find(T element)
> +    {
> +        return findById(encoder.toClient(element));
> +    }
> +
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeExpansionModel.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,47 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import org.apache.tapestry5.corelib.components.Tree;
> +
> +/**
> + * Tracks which nodes of a {@link TreeModel} are currently expanded. The
> {@linkplain DefaultTreeExpansionModel default
> + * implementation} simply stores a set of {@linkplain TreeNode#getId()
> unique node
> + * ids} to identify expanded nodes. The expansion model is updated whenever
> folders are expanded or
> + * collapsed on the client side.
> + *
> + * @since 5.3.0
> + * @see Tree
> + */
> +public interface TreeExpansionModel<T>
> +{
> +    /**
> +     * Returns true if the node has been previously {@linkplain
> #markExpanded(TreeNode) expanded}.
> +     *
> +     * @param node
> +     *            node to check for expansion
> +     * @return
> +     */
> +    boolean isExpanded(TreeNode<T> node);
> +
> +    /** Marks the node as expanded. */
> +    void markExpanded(TreeNode<T> node);
> +
> +    /** Marks the node as collapsed (not expanded). */
> +    void markCollapsed(TreeNode<T> node);
> +
> +    /** Marks all nodes as collapsed. */
> +    void clear();
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModel.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,59 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import java.util.List;
> +
> +import javax.swing.tree.TreeSelectionModel;
> +
> +import org.apache.tapestry5.ValueEncoder;
> +import org.apache.tapestry5.corelib.components.Tree;
> +
> +/**
> + * A model for tree-oriented data used by the {@link Tree} component. The
> default implemention, {@link DefaultTreeModel}
> + * uses a {@link ValueEncoder} and a {@link TreeModelAdapter} to supply the
> + * underlying information.
> + *
> + * @param <T>
> + *            type of data in the tree
> + * @since 5.3.0
> + * @see TreeSelectionModel
> + */
> +public interface TreeModel<T>
> +{
> +    /**
> +     * Returns the node or nodes that are the top level of the tree.
> +     */
> +    List<TreeNode<T>> getRootNodes();
> +
> +    /**
> +     * Locates a node in the tree by its unique id.
> +     *
> +     * @throws IllegalArgumentException
> +     *             if no such node exists
> +     * @see TreeNode#getId()
> +     */
> +    TreeNode<T> getById(String id);
> +
> +    /**
> +     * Recursively searches from the root nodes to find the tree node that
> matches
> +     * the provided element.
> +     *
> +     * @param element
> +     *            to search for
> +     * @return matching node, or null if not found
> +     */
> +    TreeNode<T> find(T element);
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeModelAdapter.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,53 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import java.util.List;
> +
> +/**
> + * Used with {@link DefaultTreeModel} to define how to extract labels and
> child nodes from a value.
> + *
> + * @since 5.3.0
> + */
> +public interface TreeModelAdapter<T>
> +{
> +    /**
> +     * Determines if the value is a leaf or a (potential) container of
> children.
> +     *
> +     * @see TreeNode#isLeaf()
> +     */
> +    boolean isLeaf(T value);
> +
> +    /**
> +     * Returns true if the value has children (only invoked for non-leaf
> values).
> +     *
> +     * @see TreeNode#getHasChildren()
> +     */
> +    boolean hasChildren(T value);
> +
> +    /**
> +     * Returns the children, in the order they should be presented to the
> client.
> +     *
> +     * @see TreeNode#getChildren()
> +     */
> +    List<T> getChildren(T value);
> +
> +    /**
> +     * Returns a text label for the value, which may be presented to the
> client.
> +     *
> +     * @see TreeNode#getLabel()
> +     */
> +    String getLabel(T value);
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/tree/TreeNode.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,72 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.tree;
> +
> +import java.util.List;
> +
> +/**
> + * A node within a {@link TreeModel}. In a {@link DefaultTreeModel}, most
> of the node's information
> + * comes via the {@link TreeModelAdapter}.
> + *
> + * @param <T>
> + *            type of node
> + * @since 5.3.0
> + */
> +public interface TreeNode<T>
> +{
> +    /**
> +     * Returns a string Id for the node that uniquely identifies it.
> +     *
> +     * @return unique string identifying the node
> +     * @see TreeModel#getById(String)
> +     */
> +    String getId();
> +
> +    /** Returns the value represented by this node. */
> +    T getValue();
> +
> +    /**
> +     * If true, then this node is a leaf node, which never has children
> (i.e., a file). If false, the node
> +     * may have children (i.e., a folder).
> +     *
> +     * @return true for leaf nodes, false for folder nodes
> +     * @see TreeModelAdapter#isLeaf(Object)
> +     */
> +    boolean isLeaf();
> +
> +    /**
> +     * Returns true if this non-leaf node has child nodes. This will not be
> invoked for leaf nodes.
> +     *
> +     * @see TreeModelAdapter#hasChildren(Object)
> +     */
> +    boolean getHasChildren();
> +
> +    /**
> +     * Returns the actual children of this non-leaf node, as additional
> nodes.
> +     *
> +     * @see TreeModelAdapter#getChildren(Object)
> +     */
> +    List<TreeNode<T>> getChildren();
> +
> +    // TODO: Some way to influence the rendered output (i.e., to display
> different icons based on
> +    // file type).
> +
> +    /**
> +     * Returns a textual label for the node. Not all UIs will make use of
> the label, but default UIs will.
> +     *
> +     * @see TreeModelAdapter#getLabel(Object)
> +     */
> +    public String getLabel();
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/corelib/components/Tree.tml
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,11 @@
> +<t:container xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
> xmlns:p="tapestry:parameter">
> +
> +  <div class="${containerClass}">
> +    <t:delegate to="renderRootNodes"/>
> +  </div>
> +
> +  <t:block id="defaultRenderTreeNodeLabel">
> +    ${node.label}
> +  </t:block>
> +
> +</t:container>
> \ No newline at end of file
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png?rev=1135351&view=auto
> ==============================================================================
> Files
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
> (added) and
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branch.png
> Tue Jun 14 00:40:55 2011 differ
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png?rev=1135351&view=auto
> ==============================================================================
> Files
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
> (added) and
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-branchend.png
> Tue Jun 14 00:40:55 2011 differ
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png?rev=1135351&view=auto
> ==============================================================================
> Files
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
> (added) and
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-sprites.png
> Tue Jun 14 00:40:55 2011 differ
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png?rev=1135351&view=auto
> ==============================================================================
> Files
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
> (added) and
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree-vpipe.png
> Tue Jun 14 00:40:55 2011 differ
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.css
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,84 @@
> +
> +DIV.t-tree-container
> +{
> +  padding: 0;
> +  margin: 0;
> +}
> +
> +DIV.t-tree-container UL
> +{
> +  list-style-type: none;
> +  background-image: url(tree-vpipe.png);
> +  background-repeat: repeat-y;
> +  margin: 0 0 0 12px;
> +  padding: 0;
> +}
> +
> +DIV.t-tree-container UL UL
> +{
> +  /* Line up the nested list's vertical bar under the element's folder
> icon. */
> +  margin: 0 0 0 24px;
> +}
> +
> +
> +DIV.t-tree-container LI {
> +  margin: 0;
> +  padding: 0 0 0 16px;
> +  background-image: url(tree-branch.png);
> +  background-repeat: no-repeat;
> +  line-height: 1.5;
> +}
> +
> +DIV.t-tree-container LI.t-last
> +{
> +    background-color: white;
> +    background-image: url(tree-branchend.png);
> +}
> +
> +/* Assume its a collapsed, but expandable, tree node. Later CSS rules
> overwrite this. */
> +
> +SPAN.t-tree-icon {
> +  display: inline-block;
> +  width: 32px;
> +  height: 16px;
> +  cursor: pointer;
> +  background-image: url(tree-sprites.png);
> +  background-position: 0px 0px;
> +}
> +
> +SPAN.t-tree-icon.t-leaf-node {
> +  cursor: default;
> +  background-position: -32px -16px;
> +}
> +
> +SPAN.t-tree-icon.t-empty-node {
> +  cursor: default;
> +  background-position: -32px 0px !important;
> +}
> +
> +SPAN.t-tree-expanded {
> +  background-position: 0px -16px;
> +}
> +
> +SPAN.t-ajax-wait {
> +  width: 16px;
> +  height: 16px;
> +  display: inline-block;
> +  background-image: url(ajax-loader.gif);
> +}
> +
> +SPAN.t-tree-icon.t-expand {
> +  width: 16px;
> +  height: 16px;
> +  background-image: url(expand.png);
> +  display: inline-block;
> +  cursor: pointer;
> +}
> +
> +SPAN.t-tree-icon.t-collapse {
> +  width: 16px;
> +  height: 16px;
> +  background-image: url(collapse.png);
> +  display: inline-block;
> +  cursor: pointer;
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tree.js
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,138 @@
> +T5.extend(T5, {
> +       Tree : {
> +
> +               /**
> +                * Approximate time per pixel for the hide and reveal
> animations. The
> +                * idea is to have small (few children) and large (many
> childen)
> +                * animations operate at the same visible rate, even though
> they will
> +                * take different amounts of time.
> +                */
> +               ANIMATION_RATE : .005,
> +
> +               /**
> +                * Maximum animation time, in seconds. This is necessary for
> very large
> +                * animations, otherwise its looks visually odd to see the
> child tree
> +                * nodes whip down the screen.
> +                */
> +               MAX_ANIMATION_DURATION : .5,
> +
> +               /** Type of Scriptaculous effect to hide/show child nodes.
> */
> +               TOGGLE_TYPE : 'blind',
> +
> +               /**
> +                * Name of Scriptaculous effects queue to ensure that
> animations do not
> +                * overwrite each other.
> +                */
> +               QUEUE_NAME : 't-tree-updates'
> +       }
> +});
> +
> +T5.extendInitializer(function() {
> +
> +       var cfg = T5.Tree;
> +
> +       function doAnimate(element) {
> +               var sublist = $(element).up('li').down("ul");
> +
> +               var dim = sublist.getDimensions();
> +
> +               var duration = Math.min(dim.height * cfg.ANIMATION_RATE,
> +                               cfg.MAX_ANIMATION_DURATION)
> +
> +               new Effect.toggle(sublist, cfg.TOGGLE_TYPE, {
> +                       duration : duration,
> +                       queue : {
> +                               position : 'end',
> +                               scope : cfg.QUEUE_NAME
> +                       }
> +               });
> +       }
> +
> +       function animateRevealChildren(element) {
> +               $(element).addClassName("t-tree-expanded");
> +
> +               doAnimate(element);
> +       }
> +
> +       function animateHideChildren(element) {
> +               $(element).removeClassName("t-tree-expanded");
> +
> +               doAnimate(element);
> +       }
> +
> +       function initializer(spec) {
> +               var loaded = spec.expanded;
> +               var expanded = spec.expanded;
> +               if (expanded) {
> +                       $(spec.clientId).addClassName("t-tree-expanded")
> +               }
> +               var loading = false;
> +
> +               function successHandler(reply) {
> +                       // Remove the Ajax load indicator
> +                       $(spec.clientId).update("");
> +                       $(spec.clientId).removeClassName("t-empty-node");
> +
> +                       var response = reply.responseJSON;
> +
> +                       Tapestry.loadScriptsInReply(response, function() {
> +                               var element = $(spec.clientId).up("li");
> +                               var label =
> element.down("span.t-tree-label");
> +
> +                               label.insert({
> +                                       after : response.content
> +                               });
> +
> +                               // Hide the new sublist so that we can
> animate revealing it.
> +                               element.down("ul").hide();
> +
> +                               animateRevealChildren(spec.clientId);
> +
> +                               loading = false;
> +                               loaded = true;
> +                               expanded = true;
> +                       });
> +
> +               }
> +
> +               function doLoad() {
> +                       if (loading)
> +                               return;
> +
> +                       loading = true;
> +
> +                       $(spec.clientId).addClassName("t-empty-node");
> +                       $(spec.clientId).update("<span
> class='t-ajax-wait'/>");
> +
> +                       Tapestry.ajaxRequest(spec.expandChildrenURL,
> successHandler);
> +               }
> +
> +               $(spec.clientId).observe("click", function(event) {
> +                       event.stop();
> +
> +                       if (!loaded) {
> +
> +                               doLoad();
> +
> +                               return;
> +                       }
> +
> +                       // Children have been loaded, just a matter of
> toggling
> +                       // between showing or hiding the children.
> +
> +                       var f = expanded ? animateHideChildren :
> animateRevealChildren;
> +
> +                       f.call(null, spec.clientId);
> +
> +                       var url = expanded ? spec.markCollapsedURL :
> spec.markExpandedURL;
> +
> +                       Tapestry.ajaxRequest(url, {});
> +
> +                       expanded = !expanded;
> +               });
> +       }
> +
> +       return {
> +               treeNode : initializer
> +       };
> +});
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FileSystemTreeModel.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,75 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.app1;
> +
> +import java.io.File;
> +import java.util.List;
> +
> +import org.apache.tapestry5.ValueEncoder;
> +import org.apache.tapestry5.func.F;
> +import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
> +import org.apache.tapestry5.tree.DefaultTreeModel;
> +import org.apache.tapestry5.tree.TreeModelAdapter;
> +
> +public class FileSystemTreeModel extends DefaultTreeModel<File>
> +{
> +    private static final ValueEncoder<File> ENCODER = new
> ValueEncoder<File>()
> +    {
> +
> +        public String toClient(File value)
> +        {
> +            return value.getPath();
> +        }
> +
> +        public File toValue(String clientValue)
> +        {
> +            return new File(clientValue);
> +        }
> +    };
> +
> +    private static final TreeModelAdapter<File> ADAPTER = new
> TreeModelAdapter<File>()
> +    {
> +
> +        public boolean isLeaf(File value)
> +        {
> +            return value.isFile();
> +        }
> +
> +        public boolean hasChildren(File value)
> +        {
> +            return value.list().length > 0;
> +        }
> +
> +        public List<File> getChildren(File value)
> +        {
> +            return CollectionFactory.newList(value.listFiles());
> +        }
> +
> +        public String getLabel(File value)
> +        {
> +            return value.getName();
> +        }
> +    };
> +
> +    public FileSystemTreeModel()
> +    {
> +        this(".");
> +    }
> +
> +    public FileSystemTreeModel(String rootDir)
> +    {
> +        super(ENCODER, ADAPTER, F.flow(new
> File(rootDir).listFiles()).toList());
> +    }
> +}
>
> Modified:
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java?rev=1135351&r1=1135350&r2=1135351&view=diff
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> (original)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
> Tue Jun 14 00:40:55 2011
> @@ -57,6 +57,8 @@ public class Index
>    private static final List<Item> ITEMS = CollectionFactory
>            .newList(
>
> +                    new Item("TreeDemo", "Tree Component Demo", "Demo of
> Tree Component"),
> +
>                    new Item("InvalidExpressionInDynamicTemplate", "Invalid
> Dynamic Expression",
>                            "Invalid expression in a Dynamic Template"),
>
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/TreeDemo.java
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,38 @@
> +// Copyright 2011 The Apache Software Foundation
> +//
> +// Licensed under the Apache License, Version 2.0 (the "License");
> +// you may not use this file except in compliance with the License.
> +// You may obtain a copy of the License at
> +//
> +// http://www.apache.org/licenses/LICENSE-2.0
> +//
> +// Unless required by applicable law or agreed to in writing, software
> +// distributed under the License is distributed on an "AS IS" BASIS,
> +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> +// See the License for the specific language governing permissions and
> +// limitations under the License.
> +
> +package org.apache.tapestry5.integration.app1.pages;
> +
> +import java.io.File;
> +
> +import org.apache.tapestry5.annotations.InjectComponent;
> +import org.apache.tapestry5.corelib.components.Tree;
> +import org.apache.tapestry5.integration.app1.FileSystemTreeModel;
> +import org.apache.tapestry5.tree.TreeModel;
> +
> +public class TreeDemo
> +{
> +    @InjectComponent
> +    private Tree fs;
> +
> +    public TreeModel<File> getFileSystemTreeModel()
> +    {
> +        return new FileSystemTreeModel();
> +    }
> +
> +    void onActionFromClear()
> +    {
> +        fs.clearExpansions();
> +    }
> +}
>
> Added:
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
> URL:
> http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml?rev=1135351&view=auto
> ==============================================================================
> ---
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
> (added)
> +++
> tapestry/tapestry5/trunk/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/pages/TreeDemo.tml
> Tue Jun 14 00:40:55 2011
> @@ -0,0 +1,15 @@
> +<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd"
> xmlns:p="tapestry:parameter">
> +
> +  <h1>Tree Demo</h1>
> +
> +  <t:tree t:id="fs" model="fileSystemTreeModel"/>
> +
> +  <p>
> +    [
> +    <t:pagelink page="treeDemo">Redraw</t:pagelink>
> +    ]
> +    [
> +    <t:actionlink t:id="clear">clear expansions</t:actionlink>
> +    ]
> +  </p>
> +</t:border>
> \ No newline at end of file
>
>
>
>
>
> --
> Best regards,
>
> Igor Drobiazko
> http://tapestry5.de
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org