You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2012/02/05 23:02:55 UTC
[5/9] moving old tree examples away,
merged recursive panel example into panel compref
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeBorder.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeBorder.java
index 0000000,0000000..fe566a4
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeBorder.java
@@@ -1,0 -1,0 +1,85 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.behavior.Behavior;
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.request.Response;
++
++/**
++ * A border for a node component which renders nested <code>DIV</code>s to simulate the structure of
++ * parental branches inside a tabular layout.
++ *
++ * @see NodeModel
++ * @see TreeColumn#populateItem(Item, String, IModel)
++ * @author svenmeier
++ */
++public class NodeBorder extends Behavior
++{
++
++ private static final long serialVersionUID = 1L;
++
++ private boolean[] branches;
++
++ public NodeBorder(boolean[] branches)
++ {
++ this.branches = branches;
++ }
++
++ @Override
++ public void beforeRender(Component component)
++ {
++ Response response = component.getResponse();
++
++ for (int i = 0; i < branches.length; i++)
++ {
++ if (i > 0)
++ {
++ response.write("<div class=\"tree-subtree\">");
++ }
++
++ if (branches[i])
++ {
++ response.write("<div class=\"tree-branch tree-branch-mid\">");
++ }
++ else
++ {
++ response.write("<div class=\"tree-branch tree-branch-last\">");
++ }
++ }
++ }
++
++ @Override
++ public void onComponentTag(Component component, ComponentTag tag)
++ {
++ tag.put("class", "tree-node");
++ }
++
++ @Override
++ public void afterRender(Component component)
++ {
++ Response response = component.getResponse();
++
++ for (int i = 0; i < branches.length; i++)
++ {
++ response.write("</div>");
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModel.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModel.java
index 0000000,0000000..b2bf466
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModel.java
@@@ -1,0 -1,0 +1,96 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import java.util.Arrays;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A model wrapping the actual node model, carrying additional information about the parental
++ * branches.
++ *
++ * @see #getBranches()
++ * @see ITreeProvider#model(Object)
++ *
++ * @author svenmeier
++ */
++public class NodeModel<T> implements IModel<T>
++{
++
++ private static final long serialVersionUID = 1L;
++
++ private IModel<T> model;
++
++ private boolean[] branches;
++
++ public NodeModel(IModel<T> model, boolean[] branches)
++ {
++ this.model = model;
++ this.branches = branches;
++ }
++
++ public IModel<T> getWrappedModel()
++ {
++ return model;
++ }
++
++ public T getObject()
++ {
++ return model.getObject();
++ }
++
++ public void setObject(T object)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public void detach()
++ {
++ model.detach();
++ }
++
++ public int getDepth()
++ {
++ return branches.length;
++ }
++
++ public boolean[] getBranches()
++ {
++ return branches;
++ }
++
++ @Override
++ public int hashCode()
++ {
++ return model.hashCode();
++ }
++
++ @Override
++ public boolean equals(Object obj)
++ {
++ if (obj instanceof NodeModel<?>)
++ {
++ NodeModel<?> nodeModel = (NodeModel<?>)obj;
++
++ return Arrays.equals(this.branches, nodeModel.branches) &&
++ this.model.equals((nodeModel).model);
++ }
++ return false;
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeColumn.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeColumn.java
index 0000000,0000000..4f086ec
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeColumn.java
@@@ -1,0 -1,0 +1,61 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator;
++import org.apache.wicket.markup.repeater.Item;
++import org.apache.wicket.model.IModel;
++
++/**
++ * @author svenmeier
++ */
++public class TreeColumn<T> extends AbstractTreeColumn<T>
++{
++
++ private static final long serialVersionUID = 1L;
++
++ public TreeColumn(IModel<String> displayModel)
++ {
++ super(displayModel);
++ }
++
++ public TreeColumn(IModel<String> displayModel, String sortProperty)
++ {
++ super(displayModel, sortProperty);
++ }
++
++ @Override
++ public String getCssClass()
++ {
++ return "tree";
++ }
++
++ public void populateItem(Item<ICellPopulator<T>> cellItem, String componentId,
++ IModel<T> rowModel)
++ {
++
++ NodeModel<T> nodeModel = (NodeModel<T>)rowModel;
++
++ Component nodeComponent = getTree().newNodeComponent(componentId,
++ nodeModel.getWrappedModel());
++
++ nodeComponent.add(new NodeBorder(nodeModel.getBranches()));
++
++ cellItem.add(nodeComponent);
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProvider.java
index 0000000,0000000..6749a4b
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProvider.java
@@@ -1,0 -1,0 +1,194 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import java.util.Iterator;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.markup.repeater.data.IDataProvider;
++import org.apache.wicket.model.IModel;
++
++/**
++ * An adapter of a {@link ITreeProvider} to a {@link IDataProvider}.
++ *
++ * @author svenmeier
++ */
++public abstract class TreeDataProvider<T> implements ITreeDataProvider<T>
++{
++ private static final long serialVersionUID = 1L;
++
++ private final ITreeProvider<T> provider;
++
++ private Branch currentBranch;
++
++ private Branch previousBranch;
++
++ private int size = -1;
++
++ public TreeDataProvider(ITreeProvider<T> provider)
++ {
++ this.provider = provider;
++ }
++
++ public long size()
++ {
++ if (size == -1)
++ {
++ size = 0;
++
++ Iterator<? extends T> iterator = iterator(0, Integer.MAX_VALUE);
++ while (iterator.hasNext())
++ {
++ iterator.next();
++
++ size++;
++ }
++ }
++ return size;
++ }
++
++ public Iterator<? extends T> iterator(long first, long count)
++ {
++ currentBranch = new Branch(null, provider.getRoots());
++
++ Iterator<T> iterator = new Iterator<T>()
++ {
++
++ public boolean hasNext()
++ {
++ while (currentBranch != null)
++ {
++ if (currentBranch.hasNext())
++ {
++ return true;
++ }
++ currentBranch = currentBranch.parent;
++ }
++
++ return false;
++ }
++
++ public T next()
++ {
++ if (!hasNext())
++ {
++ throw new IllegalStateException();
++ }
++
++ T next = currentBranch.next();
++
++ previousBranch = currentBranch;
++
++ if (iterateChildren(next))
++ {
++ currentBranch = new Branch(previousBranch, provider.getChildren(next));
++ }
++
++ return next;
++ }
++
++ public void remove()
++ {
++ throw new UnsupportedOperationException();
++ }
++ };
++
++ for (int i = 0; i < first; i++)
++ {
++ iterator.next();
++ }
++
++ return iterator;
++ }
++
++ /**
++ * Hook method to decide wether the given node's children should be iterated.
++ */
++ protected abstract boolean iterateChildren(T object);
++
++ public NodeModel<T> model(T object)
++ {
++ return previousBranch.wrapModel(provider.model(object));
++ }
++
++ public void detach()
++ {
++ currentBranch = null;
++ previousBranch = null;
++ size = -1;
++ }
++
++ private class Branch implements Iterator<T>
++ {
++ private Branch parent;
++
++ private Iterator<? extends T> children;
++
++ public Branch(Branch parent, Iterator<? extends T> children)
++ {
++ this.parent = parent;
++ this.children = children;
++ }
++
++ public NodeModel<T> wrapModel(IModel<T> model)
++ {
++ boolean[] branches = new boolean[getDepth()];
++
++ Branch branch = this;
++ for (int c = branches.length - 1; c >= 0; c--)
++ {
++ branches[c] = branch.hasNext();
++
++ branch = branch.parent;
++ }
++
++ return new NodeModel<T>(model, branches);
++ }
++
++ public int getDepth()
++ {
++ if (parent == null)
++ {
++ return 1;
++ }
++ else
++ {
++ return parent.getDepth() + 1;
++ }
++ }
++
++ public boolean hasNext()
++ {
++ return children.hasNext();
++ }
++
++ public T next()
++ {
++ if (!hasNext())
++ {
++ throw new IllegalStateException();
++ }
++
++ return children.next();
++ }
++
++ public void remove()
++ {
++ throw new UnsupportedOperationException();
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/HumanTheme.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/HumanTheme.java
index 0000000,0000000..70efe17
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/HumanTheme.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.theme;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.behavior.Behavior;
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.head.CssHeaderItem;
++import org.apache.wicket.markup.head.IHeaderResponse;
++import org.apache.wicket.request.resource.PackageResourceReference;
++import org.apache.wicket.request.resource.ResourceReference;
++
++/**
++ * @author svenmeier
++ */
++public class HumanTheme extends Behavior
++{
++ private static final long serialVersionUID = 1L;
++
++ private static final ResourceReference CSS = new PackageResourceReference(WindowsTheme.class,
++ "human/theme.css");
++
++ @Override
++ public void onComponentTag(Component component, ComponentTag tag)
++ {
++ tag.append("class", "tree-theme-human", " ");
++ }
++
++ @Override
++ public void renderHead(Component component, IHeaderResponse response)
++ {
++ response.render(CssHeaderItem.forReference(CSS));
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/WindowsTheme.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/WindowsTheme.java
index 0000000,0000000..29c8a0d
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/WindowsTheme.java
@@@ -1,0 -1,0 +1,48 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.theme;
++
++import org.apache.wicket.Component;
++import org.apache.wicket.behavior.Behavior;
++import org.apache.wicket.markup.ComponentTag;
++import org.apache.wicket.markup.head.CssHeaderItem;
++import org.apache.wicket.markup.head.IHeaderResponse;
++import org.apache.wicket.request.resource.PackageResourceReference;
++import org.apache.wicket.request.resource.ResourceReference;
++
++/**
++ * @author svenmeier
++ */
++public class WindowsTheme extends Behavior
++{
++ private static final long serialVersionUID = 1L;
++
++ private static final ResourceReference CSS = new PackageResourceReference(WindowsTheme.class,
++ "windows/theme.css");
++
++ @Override
++ public void onComponentTag(Component component, ComponentTag tag)
++ {
++ tag.append("class", "tree-theme-windows", " ");
++ }
++
++ @Override
++ public void renderHead(Component component, IHeaderResponse response)
++ {
++ response.render(CssHeaderItem.forReference(CSS));
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder-other.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder-other.gif
index 0000000,0000000..a264baa
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder-other.gif
@@@ -1,0 -1,0 +1,5 @@@
++GIF89a
++
++A�E�xԘ"�C�7iΐ�&�Anؓf��5f�
++R�C��h[���'BA z�L�B�J�*Y�8(�@O5lƁ�K�!F��1e"�飠�=hx���H�4k� (���5lڴq��
�>
++���9t�����@Aq���s�b���T �|�
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder.gif
index 0000000,0000000..55acec1
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/folder.gif
@@@ -1,0 -1,0 +1,5 @@@
++GIF89a
++
++%`�ء�mX
++
++
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/theme.css
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/theme.css
index 0000000,0000000..4cc221c
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/theme.css
@@@ -1,0 -1,0 +1,126 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++.tree-theme-human div.tree-subtree {
++ margin: 0px 0px 0px 18px;
++ padding: 0px;
++}
++
++.tree-theme-human div.tree-branch {
++ margin: 0px;
++ padding: 0px;
++}
++
++.tree-theme-human div.tree-branch-mid {
++}
++
++.tree-theme-human div.tree-branch-last {
++}
++
++/* node */
++
++.tree-theme-human span.tree-junction {
++ display: block;
++ float: left;
++ width: 18px;
++}
++
++.tree-theme-human a.tree-junction-expanded {
++ display: block;
++ float: left;
++ width: 18px;
++ background-image: url(tree.gif);
++ background-position: -18px center;
++ background-repeat: no-repeat;
++ text-decoration: none;
++}
++
++.tree-theme-human a.tree-junction-expanded:hover {
++ background-position: -54px center;
++}
++
++.tree-theme-human a.tree-junction-collapsed {
++ display: block;
++ float: left;
++ width: 18px;
++ background-image: url(tree.gif);
++ background-position: 0px center;
++ background-repeat: no-repeat;
++ text-decoration: none;
++}
++
++.tree-theme-human a.tree-junction-collapsed:hover {
++ background-position: -36px center;
++}
++
++.tree-theme-human span.tree-content {
++ display: block;
++ margin-left: 18px;
++}
++
++/* tabletree */
++
++.tree-theme-human table {
++ border-spacing: 0px;
++ border-collapse: collapse;
++}
++
++.tree-theme-human tr.selected {
++ background-color: #fad184;
++}
++
++.tree-theme-human td {
++ /* allow tree divs to fill complete td (regretfully height 100% on divs does not work) */
++ margin: 0px;
++ padding: 0px;
++}
++
++/* content */
++
++.tree-theme-human span.tree-content a {
++ color: black;
++ text-decoration: none;
++}
++
++.tree-theme-human span.tree-content a:hover {
++ text-decoration: underline;
++}
++
++.tree-theme-human span.tree-content .selected {
++ background-color: #fad184;
++}
++
++.tree-theme-human .tree-folder-closed {
++ padding-left: 18px;
++ background-image: url(folder.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
++
++.tree-theme-human .tree-folder-open {
++ padding-left: 18px;
++ background-image: url(folder.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
++
++.tree-theme-human .tree-folder-other {
++ padding-left: 18px;
++ background-image: url(folder-other.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/tree.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/tree.gif
index 0000000,0000000..ba6f173
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/human/tree.gif
@@@ -1,0 -1,0 +1,2 @@@
++GIF89aH
++
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-closed.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-closed.gif
index 0000000,0000000..eb12976
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-closed.gif
@@@ -1,0 -1,0 +1,2 @@@
++GIF89a
++-$���-|Bg " C
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-open.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-open.gif
index 0000000,0000000..c5c3110
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-open.gif
@@@ -1,0 -1,0 +1,4 @@@
++GIF89a
++������@ڿl�넙g������Pɠ!��p�r�ׄ��"�qȕ0��Ƭz����u����붶����ƚ���������
++�
++ D/���E
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-other.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-other.gif
index 0000000,0000000..42d7318
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/folder-other.gif
@@@ -1,0 -1,0 +1,2 @@@
++GIF89a
++�>>�73ʄ�<��7 #Ճ��?788߂&����2�A-�7�+���B��ף���*@`�Ç 0 ���/vB��P
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/theme.css
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/theme.css
index 0000000,0000000..12bc3c9
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/theme.css
@@@ -1,0 -1,0 +1,126 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.
++ */
++
++.tree-theme-windows div.tree-subtree {
++ margin: 0px 0px 0px 18px;
++ padding: 0px;
++}
++
++.tree-theme-windows div.tree-branch {
++ margin: 0px;
++ padding: 0px;
++}
++
++.tree-theme-windows div.tree-branch-mid {
++ background-image: url(tree.gif);
++ background-position: -54px center;
++ background-repeat: repeat-y;
++}
++
++.tree-theme-windows div.tree-branch-last {
++}
++
++/* node */
++
++.tree-theme-windows span.tree-junction {
++ display: block;
++ float: left;
++ width: 18px;
++ background-image: url(tree.gif);
++ background-position: -36px center;
++ background-repeat: no-repeat;
++}
++
++.tree-theme-windows a.tree-junction-expanded {
++ display: block;
++ float: left;
++ width: 18px;
++ background-image: url(tree.gif);
++ background-position: -18px center;
++ background-repeat: no-repeat;
++ text-decoration: none;
++}
++
++.tree-theme-windows a.tree-junction-collapsed {
++ display: block;
++ float: left;
++ width: 18px;
++ background-image: url(tree.gif);
++ background-position: 0px center;
++ background-repeat: no-repeat;
++ text-decoration: none;
++}
++
++.tree-theme-windows span.tree-content {
++ display: block;
++ margin-left: 18px;
++}
++
++/* tabletree */
++
++.tree-theme-windows table {
++ border-spacing: 0px;
++ border-collapse: collapse;
++}
++
++.tree-theme-windows tr.selected {
++ background-color: #0000ee;
++ color: #FFFFFF;
++}
++
++.tree-theme-windows td {
++ /* allow tree divs to fill complete td (regretfully height 100% on divs does not work) */
++ margin: 0px;
++ padding: 0px;
++}
++
++/* content */
++
++.tree-theme-windows span.tree-content a {
++ color: black;
++ text-decoration: none;
++}
++
++.tree-theme-windows span.tree-content a:hover {
++ text-decoration: underline;
++}
++
++.tree-theme-windows span.tree-content .selected span.tree-label {
++ background-color: #0000ee;
++ color: #FFFFFF;
++}
++
++.tree-theme-windows .tree-folder-closed {
++ padding-left: 18px;
++ background-image: url(folder-closed.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
++
++.tree-theme-windows .tree-folder-open {
++ padding-left: 18px;
++ background-image: url(folder-open.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
++
++.tree-theme-windows .tree-folder-other {
++ padding-left: 18px;
++ background-image: url(folder-other.gif);
++ background-position: 0px 50%;
++ background-repeat: no-repeat;
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/tree.gif
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/tree.gif
index 0000000,0000000..68fe235
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/theme/windows/tree.gif
@@@ -1,0 -1,0 +1,17 @@@
++GIF89aH
++
++�"�G�"�K*#�O:9#�SJY#�WZy��6v�喡�f�cYf�g6�f�kV�f�o���s�Y��w����{�٧���ڠ�y�嗆ƙ蟋
++Z(��*���R:)��bz��9Zꩦ�r*&���Jf��v��:��t�j��xҪ��|�ꧮ��z*������[���"�����2�+��:,���
++l�Jm��n+-��jkm�ݎ����*�ծ������.�������{����������';��/{�� 7���
?������f����r���������o�r�+�r�/�s�3�\s�,���0�,��4m��8�lt�G��t�K�t�HG���LS��PO�u�[_�u�\���`�mv�hS���7�M��OÍ��_�M��g㝶���6���6��6�7�+>7�;^7��K~7�[�7�k���z�����^z�'�z�G�z�g{糓^�귳���������?������������<��/o���c=��s���}k/���o:�蛏z�ȇ���߷���ӯ��������/��ē��
++P�
++ԧ�A��P����5jN��S��ԩ?�jP�:T���)�jR�:S�"5�_�)WÚձj�g�W�Zֵ���n��M��չ�ծr=*]�ZW��կy]�_ئְ�}�a�ب.ֱ���c%٪Nֲ��jf��Y�^V�x
-`EKX�"ִ�E-dUKY�bֵ��lK;���6��]mn[����6��
�m��[��ָ��E�o���:���=nt�;��V���ͮt�K�
++ِ���8���o����I�h�)����ֆbX�☑&� ��Ƒ*�n,y�$��0��z8�!��(�-�n8��h�+Y�9ّ>��@��BY�y�<��4وLo=�����6�7�Y �C��R��AɕJ��VYoX�Uٕeyog��bI�����֖M��[��c��r�ot��v��q�����:���֗W �n��p����o�i��Y��y��I�D٘�8��i��i�������镉���y��Y�������2ٚ����������l9�~Y��i�����ٛ����y��y����Y���F���������ɚș�й�ҹ��I��i�ع�dٝsɛ�Y�������ٞ������䩗�ɗ�9��Y��9��Y��y������ٟ�9������٠�ɞ�ɠ
++Z�z����$ʢ�
++��9p�y��9�J�j�!��
++�ʣ�
++�&*�(J�)Z�+ʡ-Z�/z��Yj�7��1*�3J�5ʥC�9Zp;��=*�?*�AJ�Oj�E��G��IʦK�M
++�[ʤW��_��S��U�]
++�gzpi:�k
++�mJ�cj�e��s��uʨw�a�y
++�o*�qJ�Q*�aʩ{�}��ʧ�j������������p�j��*��ꪑ
++��*�����j��������ʫ�ꫡʪ�j�����J��j�������~�
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSet.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSet.java
index 0000000,0000000..ad827ee
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSet.java
@@@ -1,0 -1,0 +1,143 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.util.Collection;
++import java.util.Iterator;
++import java.util.Set;
++
++import org.apache.wicket.model.IDetachable;
++
++/**
++ * An inverse set.
++ *
++ * @author svenmeier
++ */
++public class InverseSet<T> implements Set<T>, IDetachable
++{
++
++ private static final long serialVersionUID = 1L;
++
++ private Set<T> set;
++
++ /**
++ * Create a full set.
++ *
++ * @param set
++ * the contained set
++ */
++ public InverseSet(Set<T> set)
++ {
++ this.set = set;
++ }
++
++ public void detach()
++ {
++ if (set instanceof IDetachable)
++ {
++ ((IDetachable)set).detach();
++ }
++ }
++
++ public boolean isEmpty()
++ {
++ return !set.isEmpty();
++ }
++
++ public boolean contains(Object o)
++ {
++ return !set.contains(o);
++ }
++
++ public boolean add(T t)
++ {
++ return set.remove(t);
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean remove(Object o)
++ {
++ return set.add((T)o);
++ }
++
++ public boolean addAll(Collection<? extends T> ts)
++ {
++ boolean changed = false;
++
++ for (T t : ts)
++ {
++ changed |= set.remove(t);
++ }
++
++ return changed;
++ }
++
++ public boolean containsAll(Collection<?> cs)
++ {
++ for (Object c : cs)
++ {
++ if (set.contains(c))
++ {
++ return false;
++ }
++ }
++ return true;
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean removeAll(Collection<?> cs)
++ {
++ boolean changed = false;
++
++ for (Object c : cs)
++ {
++ changed |= set.add((T)c);
++ }
++
++ return changed;
++ }
++
++ public int size()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public void clear()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public Iterator<T> iterator()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public boolean retainAll(Collection<?> c)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public Object[] toArray()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public <S> S[] toArray(S[] a)
++ {
++ throw new UnsupportedOperationException();
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/KeyingSet.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/KeyingSet.java
index 0000000,0000000..7ec798c
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/KeyingSet.java
@@@ -1,0 -1,0 +1,164 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.io.Serializable;
++import java.util.Collection;
++import java.util.HashSet;
++import java.util.Iterator;
++import java.util.Set;
++
++import org.apache.wicket.model.AbstractReadOnlyModel;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A set holding objects by their keys.
++ *
++ * Note: Apart from {@link #contains(Object)} no query methods are supported.
++ *
++ * @see #key(Object)
++ *
++ * @author svenmeier
++ */
++public abstract class KeyingSet<T> implements Set<T>, Serializable
++{
++
++ private Set<Object> keys = new HashSet<Object>();
++
++ /**
++ * Get the key for the given object.
++ *
++ * @param t
++ * object to get key for
++ */
++ protected abstract Object key(T t);
++
++ public int size()
++ {
++ return keys.size();
++ }
++
++ public boolean isEmpty()
++ {
++ return keys.isEmpty();
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean contains(Object o)
++ {
++ Object key = key((T)o);
++ if (key == null)
++ {
++ return false;
++ }
++ else
++ {
++ return keys.contains(key);
++ }
++ }
++
++ public boolean add(T t)
++ {
++ return keys.add(key(t));
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean remove(Object t)
++ {
++ return keys.remove(key((T)t));
++ }
++
++ public boolean containsAll(Collection<?> cs)
++ {
++ for (Object c : cs)
++ {
++ if (!contains(c))
++ {
++ return false;
++ }
++ }
++ return true;
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean addAll(Collection<? extends T> cs)
++ {
++ boolean changed = false;
++
++ for (Object c : cs)
++ {
++ changed |= add((T)c);
++ }
++
++ return changed;
++ }
++
++ @SuppressWarnings("unchecked")
++ public boolean removeAll(Collection<?> cs)
++ {
++ boolean changed = false;
++
++ for (Object c : cs)
++ {
++ changed |= remove(c);
++ }
++
++ return changed;
++ }
++
++ public void clear()
++ {
++ keys.clear();
++ }
++
++ public Iterator<T> iterator()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public Object[] toArray()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public <S> S[] toArray(S[] ts)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public boolean retainAll(Collection<?> c)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ /**
++ * Create a model holding this set.
++ *
++ * @return model
++ */
++ public IModel<Set<T>> createModel()
++ {
++ return new AbstractReadOnlyModel<Set<T>>()
++ {
++ @Override
++ public Set<T> getObject()
++ {
++ return KeyingSet.this;
++ }
++ };
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubset.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubset.java
index 0000000,0000000..43b411d
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubset.java
@@@ -1,0 -1,0 +1,242 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.util.Collection;
++import java.util.HashSet;
++import java.util.Iterator;
++import java.util.Set;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.AbstractReadOnlyModel;
++import org.apache.wicket.model.IDetachable;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A subset of a {@link ITreeProvider}'s tree offering automatic detachment.
++ *
++ * Make sure that the containing model calls {@link IDetachable#detach()} on its model object.
++ *
++ * @see ITreeProvider#model(Object)
++ *
++ * @author svenmeier
++ */
++public class ProviderSubset<T> implements Set<T>, IDetachable
++{
++
++ private static final long serialVersionUID = 1L;
++
++ private ITreeProvider<T> provider;
++
++ private Set<IModel<T>> models = new HashSet<IModel<T>>();
++
++ /**
++ * Create an empty subset.
++ *
++ * @param provider
++ * the provider of the complete set
++ */
++ public ProviderSubset(ITreeProvider<T> provider)
++ {
++ this(provider, false);
++ }
++
++ /**
++ * Create a subset optionally containing all roots of the provider.
++ *
++ * @param provider
++ * the provider of the complete set
++ * @param addRoots
++ * should all roots be added to this subset
++ */
++ public ProviderSubset(ITreeProvider<T> provider, boolean addRoots)
++ {
++ this.provider = provider;
++
++ if (addRoots)
++ {
++ Iterator<? extends T> roots = provider.getRoots();
++ while (roots.hasNext())
++ {
++ add(roots.next());
++ }
++ }
++ }
++
++ public void detach()
++ {
++ for (IModel<T> model : models)
++ {
++ model.detach();
++ }
++ }
++
++ public int size()
++ {
++ return models.size();
++ }
++
++ public boolean isEmpty()
++ {
++ return models.size() == 0;
++ }
++
++ public void clear()
++ {
++ detach();
++
++ models.clear();
++ }
++
++ public boolean contains(Object o)
++ {
++ IModel<T> model = model(o);
++
++ boolean contains = models.contains(model);
++
++ model.detach();
++
++ return contains;
++ }
++
++ public boolean add(T t)
++ {
++ return models.add(model(t));
++ }
++
++ public boolean remove(Object o)
++ {
++ IModel<T> model = model(o);
++
++ boolean removed = models.remove(model);
++
++ model.detach();
++
++ return removed;
++ }
++
++ public Iterator<T> iterator()
++ {
++ return new Iterator<T>()
++ {
++
++ private Iterator<IModel<T>> iterator = models.iterator();
++
++ private IModel<T> current;
++
++ public boolean hasNext()
++ {
++ return iterator.hasNext();
++ }
++
++ public T next()
++ {
++ current = iterator.next();
++
++ return current.getObject();
++ }
++
++ public void remove()
++ {
++ iterator.remove();
++
++ current.detach();
++ current = null;
++ }
++ };
++ }
++
++ public boolean addAll(Collection<? extends T> ts)
++ {
++ boolean changed = false;
++
++ for (T t : ts)
++ {
++ changed |= add(t);
++ }
++
++ return changed;
++ }
++
++ public boolean containsAll(Collection<?> cs)
++ {
++ for (Object c : cs)
++ {
++ if (!contains(c))
++ {
++ return false;
++ }
++ }
++ return true;
++ }
++
++ public boolean removeAll(Collection<?> cs)
++ {
++ boolean changed = false;
++
++ for (Object c : cs)
++ {
++ changed |= remove(c);
++ }
++
++ return changed;
++ }
++
++ public boolean retainAll(Collection<?> c)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public Object[] toArray()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public <S> S[] toArray(S[] a)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ @SuppressWarnings("unchecked")
++ private IModel<T> model(Object o)
++ {
++ return provider.model((T)o);
++ }
++
++ /**
++ * Create a model holding this set.
++ *
++ * @return model
++ */
++ public IModel<Set<T>> createModel()
++ {
++ return new AbstractReadOnlyModel<Set<T>>()
++ {
++ @Override
++ public Set<T> getObject()
++ {
++ return ProviderSubset.this;
++ }
++
++ @Override
++ public void detach()
++ {
++ ProviderSubset.this.detach();
++ }
++ };
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/SortableTreeProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/SortableTreeProvider.java
index 0000000,0000000..92d9e91
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/SortableTreeProvider.java
@@@ -1,0 -1,0 +1,97 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState;
++import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider;
++import org.apache.wicket.extensions.markup.html.repeater.tree.ISortableTreeProvider;
++import org.apache.wicket.extensions.markup.html.repeater.util.SingleSortState;
++import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
++
++/**
++ * Convenience implementation of a tree provider that can also act as a locator for a
++ * {@link SingleSortState} object.
++ *
++ * @author svenmeier
++ */
++public abstract class SortableTreeProvider<T> implements ISortableTreeProvider<T>
++{
++ private SingleSortState state = new SingleSortState();
++
++ /**
++ * @see ISortableDataProvider#getSortState()
++ */
++ public final ISortState getSortState()
++ {
++ return state;
++ }
++
++ /**
++ * @see ISortableDataProvider#setSortState(org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortState)
++ */
++ public final void setSortState(ISortState state)
++ {
++ if (!(state instanceof SingleSortState))
++ {
++ throw new IllegalArgumentException(
++ "argument [state] must be an instance of SingleSortState, but it is [" +
++ state.getClass().getName() + "]:[" + state.toString() + "]");
++ }
++ this.state = (SingleSortState)state;
++ }
++
++ /**
++ * Returns current sort state
++ *
++ * @return current sort state
++ */
++ public SortParam getSort()
++ {
++ return state.getSort();
++ }
++
++ /**
++ * Sets the current sort state
++ *
++ * @param param
++ * parameter containing new sorting information
++ */
++ public void setSort(SortParam param)
++ {
++ state.setSort(param);
++ }
++
++ /**
++ * Sets the current sort state
++ *
++ * @param property
++ * sort property
++ * @param ascending
++ * sort direction
++ */
++ public void setSort(String property, boolean ascending)
++ {
++ setSort(new SortParam(property, ascending));
++ }
++
++ /**
++ * @see ISortableDataProvider#detach()
++ */
++ public void detach()
++ {
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProvider.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProvider.java
index 0000000,0000000..d84c1dd
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/main/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProvider.java
@@@ -1,0 -1,0 +1,258 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.io.Serializable;
++import java.util.ArrayList;
++import java.util.Iterator;
++import java.util.List;
++
++import javax.swing.event.TreeModelEvent;
++import javax.swing.event.TreeModelListener;
++import javax.swing.tree.TreeModel;
++
++import org.apache.wicket.ajax.AjaxRequestTarget;
++import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree;
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.IModel;
++
++/**
++ * A provider wrapping a Swing {@link TreeModel}.
++ *
++ * EXPERIMENTAL !
++ *
++ * @author svenmeier
++ * @param <T>
++ * model object type
++ */
++public abstract class TreeModelProvider<T> implements ITreeProvider<T>
++{
++
++ private static final long serialVersionUID = 1L;
++
++ private final TreeModel treeModel;
++
++ private final boolean rootVisible;
++
++ protected boolean completeUpdate;
++
++ protected List<T> nodeUpdates;
++
++ protected List<T> branchUpdates;
++
++ /**
++ * Wrap the given {@link TreeModel}.
++ *
++ * @param treeModel
++ * model to wrap
++ */
++ public TreeModelProvider(TreeModel treeModel)
++ {
++ this(treeModel, true);
++ }
++
++ /**
++ * Wrap the given {@link TreeModel}.
++ *
++ * @param treeModel
++ * the wrapped model
++ * @param rootVisible
++ * should the root be visible
++ */
++ public TreeModelProvider(TreeModel treeModel, boolean rootVisible)
++ {
++ this.treeModel = treeModel;
++ this.rootVisible = rootVisible;
++
++ treeModel.addTreeModelListener(new Listener());
++ }
++
++ public Iterator<T> getRoots()
++ {
++ if (rootVisible)
++ {
++ return new Iterator<T>()
++ {
++ boolean next = true;
++
++ public boolean hasNext()
++ {
++ return next;
++ }
++
++ public T next()
++ {
++ next = false;
++ return cast(treeModel.getRoot());
++ }
++
++ public void remove()
++ {
++ throw new UnsupportedOperationException();
++ }
++ };
++ }
++ else
++ {
++ return getChildren(cast(treeModel.getRoot()));
++ }
++ }
++
++ public boolean hasChildren(T object)
++ {
++ return !treeModel.isLeaf(object);
++ }
++
++ public Iterator<T> getChildren(final T object)
++ {
++ return new Iterator<T>()
++ {
++ private int size = treeModel.getChildCount(object);
++ private int index = -1;
++
++ public boolean hasNext()
++ {
++ return index < size - 1;
++ }
++
++ public T next()
++ {
++ index++;
++ return cast(treeModel.getChild(object, index));
++ }
++
++ public void remove()
++ {
++ throw new UnsupportedOperationException();
++ }
++ };
++ }
++
++ @SuppressWarnings("unchecked")
++ protected T cast(Object object)
++ {
++ return (T)object;
++ }
++
++ public abstract IModel<T> model(T object);
++
++ public void detach()
++ {
++ completeUpdate = false;
++ nodeUpdates = null;
++ branchUpdates = null;
++ }
++
++ /**
++ * Call this method after all change to the wrapped {@link TreeModel} being initiated via
++ * {@link AjaxRequestTarget}.
++ *
++ * @param tree
++ * the tree utilizing this {@link ITreeProvider}
++ * @param target
++ * the {@link AjaxRequestTarget} which initiated the changes
++ */
++ public void update(AbstractTree<T> tree, AjaxRequestTarget target)
++ {
++ if (completeUpdate)
++ {
++ target.add(tree);
++ }
++ else
++ {
++ if (nodeUpdates != null)
++ {
++ for (T object : nodeUpdates)
++ {
++ tree.updateNode(object, target);
++ }
++ }
++
++ if (branchUpdates != null)
++ {
++ for (T object : branchUpdates)
++ {
++ tree.updateBranch(object, target);
++ }
++ }
++ }
++
++ detach();
++ }
++
++ protected void nodeUpdate(Object[] nodes)
++ {
++ if (nodeUpdates == null)
++ {
++ nodeUpdates = new ArrayList<T>();
++ }
++
++ for (Object node : nodes)
++ {
++ nodeUpdates.add(cast(node));
++ }
++ }
++
++ protected void branchUpdate(Object branch)
++ {
++ if (branchUpdates == null)
++ {
++ branchUpdates = new ArrayList<T>();
++ }
++
++ branchUpdates.add(cast(branch));
++ }
++
++ private class Listener implements TreeModelListener, Serializable
++ {
++ private static final long serialVersionUID = 1L;
++
++ public void treeNodesChanged(TreeModelEvent e)
++ {
++ if (e.getChildIndices() == null)
++ {
++ completeUpdate = true;
++ }
++ else
++ {
++ nodeUpdate(e.getChildren());
++ }
++ }
++
++ public void treeNodesInserted(TreeModelEvent e)
++ {
++ branchUpdate(e.getTreePath().getLastPathComponent());
++ }
++
++ public void treeNodesRemoved(TreeModelEvent e)
++ {
++ branchUpdate(e.getTreePath().getLastPathComponent());
++ }
++
++ public void treeStructureChanged(TreeModelEvent e)
++ {
++ if (e.getTreePath().getPathCount() == 1)
++ {
++ completeUpdate = true;
++ }
++ else
++ {
++ branchUpdate(e.getTreePath().getLastPathComponent());
++ }
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModelTest.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModelTest.java
index 0000000,0000000..9b127e4
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/NodeModelTest.java
@@@ -1,0 -1,0 +1,87 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import junit.framework.Assert;
++
++import org.apache.wicket.model.IModel;
++import org.junit.Test;
++
++/**
++ * Test for {@link NodeModel}.
++ *
++ * @author svenmeier
++ */
++public class NodeModelTest extends Assert
++{
++ /**
++ * Test equality.
++ */
++ @Test
++ public void equality()
++ {
++ NodeModel<String> model = new NodeModel<String>(new StringModel("A"), new boolean[] { true,
++ false });
++
++ assertTrue(model.equals(new NodeModel<String>(new StringModel("A"), new boolean[] { true,
++ false })));
++
++ assertFalse(model.equals(new NodeModel<String>(new StringModel("A"), new boolean[] { true,
++ true })));
++
++ assertFalse(model.equals(new NodeModel<String>(new StringModel("B"), new boolean[] { true,
++ false })));
++ }
++
++ private class StringModel implements IModel<String>
++ {
++
++ private static final long serialVersionUID = 1L;
++
++ private String string;
++
++ public StringModel(String string)
++ {
++ this.string = string;
++ }
++
++ public String getObject()
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public void setObject(String object)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public void detach()
++ {
++ }
++
++ @Override
++ public boolean equals(Object obj)
++ {
++ if (obj instanceof StringModel)
++ {
++ return string.equals(((StringModel)obj).string);
++ }
++
++ return false;
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProviderTest.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProviderTest.java
index 0000000,0000000..5e794fb
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/table/TreeDataProviderTest.java
@@@ -1,0 -1,0 +1,128 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.table;
++
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Iterator;
++import java.util.List;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.model.Model;
++import org.junit.Assert;
++import org.junit.Test;
++
++/**
++ * Test for {@link TreeDataProvider}.
++ */
++public class TreeDataProviderTest extends Assert
++{
++
++ /**
++ * Test iterator.
++ */
++ @Test
++ public void iterator()
++ {
++ TreeDataProvider<String> provider = new TreeDataProvider<String>(new TestProvider())
++ {
++ private static final long serialVersionUID = 1L;
++
++ @Override
++ protected boolean iterateChildren(String object)
++ {
++ return !object.startsWith("B");
++ }
++ };
++
++ assertEquals(3 + 2 * (3 + 3 * 3), provider.size());
++
++ Iterator<? extends String> iterator = provider.iterator(0, Integer.MAX_VALUE);
++ assertEquals("A", iterator.next());
++ assertEquals("AA", iterator.next());
++ assertEquals("AAA", iterator.next());
++ assertEquals("AAB", iterator.next());
++ assertEquals("AAC", iterator.next());
++ assertEquals("AB", iterator.next());
++ assertEquals("ABA", iterator.next());
++ assertEquals("ABB", iterator.next());
++ assertEquals("ABC", iterator.next());
++ assertEquals("AC", iterator.next());
++ assertEquals("ACA", iterator.next());
++ assertEquals("ACB", iterator.next());
++ assertEquals("ACC", iterator.next());
++ assertEquals("B", iterator.next());
++ // note: B is not iterated (i.e. collapsed)!
++ assertEquals("C", iterator.next());
++ assertEquals("CA", iterator.next());
++ assertEquals("CAA", iterator.next());
++ assertEquals("CAB", iterator.next());
++ assertEquals("CAC", iterator.next());
++ assertEquals("CB", iterator.next());
++ assertEquals("CBA", iterator.next());
++ assertEquals("CBB", iterator.next());
++ assertEquals("CBC", iterator.next());
++ assertEquals("CC", iterator.next());
++ assertEquals("CCA", iterator.next());
++ assertEquals("CCB", iterator.next());
++ assertEquals("CCC", iterator.next());
++
++ assertFalse(iterator.hasNext());
++ }
++
++ private static class TestProvider implements ITreeProvider<String>
++ {
++
++ private static final long serialVersionUID = 1L;
++
++ public Iterator<? extends String> getRoots()
++ {
++ return Arrays.asList("A", "B", "C").iterator();
++ }
++
++ public Iterator<? extends String> getChildren(String object)
++ {
++ List<String> children = new ArrayList<String>();
++
++ if (hasChildren(object))
++ {
++ for (int i = 0; i < 3; i++)
++ {
++ children.add(object + (char)('A' + i));
++ }
++ }
++
++ return children.iterator();
++ }
++
++
++ public boolean hasChildren(String object)
++ {
++ return object.length() < 3;
++ }
++
++ public IModel<String> model(String object)
++ {
++ return Model.of(object);
++ }
++
++ public void detach()
++ {
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSetTest.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSetTest.java
index 0000000,0000000..bcf4279
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/InverseSetTest.java
@@@ -1,0 -1,0 +1,77 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.util.HashSet;
++
++import junit.framework.Assert;
++
++import org.apache.wicket.model.IDetachable;
++import org.junit.Test;
++
++/**
++ * Test for {@link InverseSet}.
++ *
++ * @author svenmeier
++ */
++public class InverseSetTest extends Assert
++{
++ private TestSet set;
++
++ /**
++ * Construct.
++ */
++ public InverseSetTest()
++ {
++ set = new TestSet();
++ set.add("A");
++ }
++
++ /**
++ * Test contains.
++ */
++ @Test
++ public void contains()
++ {
++ InverseSet<String> inverse = new InverseSet<String>(set);
++ assertFalse(inverse.contains("A"));
++ assertTrue(inverse.contains("B"));
++
++ inverse.remove("B");
++ assertFalse(inverse.contains("A"));
++ assertFalse(inverse.contains("B"));
++
++ inverse.add("A");
++ assertTrue(inverse.contains("A"));
++ assertFalse(inverse.contains("B"));
++
++ inverse.detach();
++ assertTrue(set.detached);
++ }
++
++ private class TestSet extends HashSet<String> implements IDetachable
++ {
++ private static final long serialVersionUID = 1L;
++
++ boolean detached = false;
++
++ public void detach()
++ {
++ detached = true;
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubsetTest.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubsetTest.java
index 0000000,0000000..21ce444
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/ProviderSubsetTest.java
@@@ -1,0 -1,0 +1,170 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.util.ArrayList;
++import java.util.Arrays;
++import java.util.Iterator;
++import java.util.List;
++
++import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
++import org.apache.wicket.model.IModel;
++import org.junit.Assert;
++import org.junit.Test;
++
++/**
++ * Test for {@link ProviderSubset}.
++ *
++ * @author svenmeier
++ */
++public class ProviderSubsetTest extends Assert
++{
++ private ITreeProvider<String> provider = new EmptyProvider();
++
++ /**
++ * All models requested from the provider.
++ */
++ private List<StringModel> models = new ArrayList<StringModel>();
++
++ /**
++ * Test set methods.
++ */
++ @Test
++ public void setMethods()
++ {
++ ProviderSubset<String> subset = new ProviderSubset<String>(provider);
++
++ subset.add("A");
++ subset.addAll(Arrays.asList("AA", "AAA"));
++
++ assertEquals(3, subset.size());
++
++ Iterator<String> iterator = subset.iterator();
++ assertTrue(iterator.hasNext());
++ iterator.next();
++ assertTrue(iterator.hasNext());
++ iterator.next();
++ assertTrue(iterator.hasNext());
++ iterator.next();
++ assertFalse(iterator.hasNext());
++ try
++ {
++ iterator.next();
++ fail();
++ }
++ catch (Exception expected)
++ {
++ }
++
++ assertTrue(subset.contains("A"));
++ assertTrue(subset.contains("AA"));
++ assertTrue(subset.contains("AAA"));
++
++ subset.createModel().detach();
++
++ for (StringModel model : models)
++ {
++ assertTrue(model.isDetached());
++ }
++
++ assertTrue(subset.contains("A"));
++ assertTrue(subset.contains("AA"));
++ assertTrue(subset.contains("AAA"));
++ }
++
++ private class StringModel implements IModel<String>
++ {
++
++ private static final long serialVersionUID = 1L;
++
++ private String string;
++
++ private boolean detached;
++
++ public StringModel(String string)
++ {
++ this.string = string;
++ models.add(this);
++ }
++
++ public String getObject()
++ {
++ detached = false;
++ return string;
++ }
++
++ public void setObject(String string)
++ {
++ detached = false;
++ this.string = string;
++ }
++
++ public void detach()
++ {
++ detached = true;
++ }
++
++ public boolean isDetached()
++ {
++ return detached;
++ }
++
++ @Override
++ public boolean equals(Object obj)
++ {
++ return string == ((StringModel)obj).string;
++ }
++
++ @Override
++ public int hashCode()
++ {
++ return string.hashCode();
++ }
++ }
++
++ private class EmptyProvider implements ITreeProvider<String>
++ {
++
++ private static final long serialVersionUID = 1L;
++
++ private List<String> EMPTY = new ArrayList<String>();
++
++ public Iterator<String> getRoots()
++ {
++ return EMPTY.iterator();
++ }
++
++ public boolean hasChildren(String object)
++ {
++ return false;
++ }
++
++ public Iterator<String> getChildren(String string)
++ {
++ throw new UnsupportedOperationException();
++ }
++
++ public IModel<String> model(String string)
++ {
++ return new StringModel(string);
++ }
++
++ public void detach()
++ {
++ }
++ }
++}
http://git-wip-us.apache.org/repos/asf/wicket/blob/05840d1c/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProviderTest.java
----------------------------------------------------------------------
diff --cc wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProviderTest.java
index 0000000,0000000..6851112
new file mode 100644
--- /dev/null
+++ b/wicket-extensions/src/test/java/org/apache/wicket/extensions/markup/html/repeater/tree/util/TreeModelProviderTest.java
@@@ -1,0 -1,0 +1,155 @@@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You 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.wicket.extensions.markup.html.repeater.tree.util;
++
++import java.util.Iterator;
++
++import javax.swing.tree.DefaultMutableTreeNode;
++import javax.swing.tree.DefaultTreeModel;
++import javax.swing.tree.MutableTreeNode;
++
++import org.apache.wicket.model.IModel;
++import org.apache.wicket.model.Model;
++import org.junit.Assert;
++import org.junit.Test;
++
++/**
++ * Test for {@link TreeModelProvider}.
++ *
++ * @author svenmeier
++ */
++public class TreeModelProviderTest extends Assert
++{
++ private DefaultMutableTreeNode root;
++
++ private DefaultTreeModel treeModel;
++
++ /**
++ * Construct.
++ */
++ public TreeModelProviderTest()
++ {
++ root = new DefaultMutableTreeNode("JTree");
++ DefaultMutableTreeNode parent;
++
++ parent = new DefaultMutableTreeNode("colors");
++ root.add(parent);
++ parent.add(new DefaultMutableTreeNode("blue"));
++ parent.add(new DefaultMutableTreeNode("violet"));
++ parent.add(new DefaultMutableTreeNode("red"));
++ parent.add(new DefaultMutableTreeNode("yellow"));
++
++ parent = new DefaultMutableTreeNode("sports");
++ root.add(parent);
++ parent.add(new DefaultMutableTreeNode("basketball"));
++ parent.add(new DefaultMutableTreeNode("soccer"));
++ parent.add(new DefaultMutableTreeNode("football"));
++ parent.add(new DefaultMutableTreeNode("hockey"));
++
++ parent = new DefaultMutableTreeNode("food");
++ root.add(parent);
++ parent.add(new DefaultMutableTreeNode("hot dogs"));
++ parent.add(new DefaultMutableTreeNode("pizza"));
++ parent.add(new DefaultMutableTreeNode("ravioli"));
++ parent.add(new DefaultMutableTreeNode("bananas"));
++
++ treeModel = new DefaultTreeModel(root);
++ }
++
++ /**
++ * Test roots and children.
++ */
++ @Test
++ public void rootsAndChildren()
++ {
++ TreeModelProvider<DefaultMutableTreeNode> provider = new TreeModelProvider<DefaultMutableTreeNode>(
++ treeModel)
++ {
++ private static final long serialVersionUID = 1L;
++
++ @Override
++ public IModel<DefaultMutableTreeNode> model(DefaultMutableTreeNode object)
++ {
++ return Model.of(object);
++ }
++ };
++
++ Iterator<DefaultMutableTreeNode> roots = provider.getRoots();
++ assertTrue(roots.hasNext());
++ DefaultMutableTreeNode root = roots.next();
++ assertEquals("JTree", root.getUserObject());
++ assertFalse(roots.hasNext());
++
++ Iterator<DefaultMutableTreeNode> children = provider.getChildren(root);
++ assertTrue(children.hasNext());
++ assertEquals("colors", children.next().getUserObject());
++ assertTrue(children.hasNext());
++ assertEquals("sports", children.next().getUserObject());
++ assertTrue(children.hasNext());
++ assertEquals("food", children.next().getUserObject());
++ assertFalse(roots.hasNext());
++
++ treeModel.nodeChanged(root);
++ }
++
++ /**
++ * Test updating.
++ */
++ @Test
++ public void update()
++ {
++ TreeModelProvider<DefaultMutableTreeNode> provider = new TreeModelProvider<DefaultMutableTreeNode>(
++ treeModel)
++ {
++ private static final long serialVersionUID = 1L;
++
++ @Override
++ public IModel<DefaultMutableTreeNode> model(DefaultMutableTreeNode object)
++ {
++ return Model.of(object);
++ }
++ };
++
++ assertFalse(provider.completeUpdate);
++ assertEquals(null, provider.nodeUpdates);
++ assertEquals(null, provider.branchUpdates);
++
++ treeModel.removeNodeFromParent((MutableTreeNode)root.getChildAt(0).getChildAt(0));
++
++ assertFalse(provider.completeUpdate);
++ assertEquals(null, provider.nodeUpdates);
++ assertEquals(1, provider.branchUpdates.size());
++
++ treeModel.nodeChanged(root.getChildAt(1));
++
++ assertFalse(provider.completeUpdate);
++ assertEquals(1, provider.nodeUpdates.size());
++ assertEquals(1, provider.branchUpdates.size());
++
++ treeModel.nodeStructureChanged(root.getChildAt(2));
++
++ assertFalse(provider.completeUpdate);
++ assertEquals(1, provider.nodeUpdates.size());
++ assertEquals(2, provider.branchUpdates.size());
++
++ treeModel.setRoot(new DefaultMutableTreeNode("bam!"));
++
++ assertTrue(provider.completeUpdate);
++ assertEquals(1, provider.nodeUpdates.size());
++ assertEquals(2, provider.branchUpdates.size());
++ }
++}