001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.extensions.markup.html.repeater.tree;
018
019import java.util.Set;
020
021import org.apache.wicket.Component;
022import org.apache.wicket.core.request.handler.IPartialPageRequestHandler;
023import org.apache.wicket.extensions.markup.html.repeater.tree.nested.BranchItem;
024import org.apache.wicket.extensions.markup.html.repeater.tree.nested.Subtree;
025import org.apache.wicket.model.IModel;
026import org.apache.wicket.util.visit.IVisit;
027import org.apache.wicket.util.visit.IVisitor;
028
029/**
030 * A tree with nested markup.
031 * 
032 * @author svenmeier
033 * @param <T>
034 *            the model object type
035 */
036public abstract class NestedTree<T> extends AbstractTree<T>
037{
038        private static final long serialVersionUID = 1L;
039
040        /**
041         * Construct.
042         * 
043         * @param id
044         *            the component id
045         * @param provider
046         *            the provider of the tree
047         */
048        public NestedTree(String id, ITreeProvider<T> provider)
049        {
050                this(id, provider, null);
051        }
052
053        /**
054         * Construct.
055         * 
056         * @param id
057         *            the component id
058         * @param provider
059         *            the provider of the tree
060         * @param state
061         *            the expansion state
062         * 
063         * @see org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree.State
064         */
065        public NestedTree(String id, ITreeProvider<T> provider, IModel<? extends Set<T>> state)
066        {
067                super(id, provider, state);
068
069                add(newSubtree("subtree", new RootsModel()));
070        }
071
072        /**
073         * Create a new subtree.
074         * 
075         * @param id
076         *            component id
077         * @param model
078         *            the model of the new subtree
079         * @return the created component
080         */
081        public Component newSubtree(String id, IModel<T> model)
082        {
083                return new Subtree<>(id, this, model);
084        }
085
086        /**
087         * Overridden to let the node output its markup id.
088         * 
089         * @see #updateNode(Object, IPartialPageRequestHandler)
090         * @see Component#setOutputMarkupId(boolean)
091         */
092        @Override
093        public Component newNodeComponent(String id, IModel<T> model)
094        {
095                Component node = super.newNodeComponent(id, model);
096
097                node.setOutputMarkupId(true);
098
099                return node;
100        }
101
102        /**
103         * Overridden to update the corresponding {@link BranchItem} only.
104         *
105         * @param t
106         *            node to update
107         * @param target
108         *            request target must not be @code null}
109         */
110        @Override
111        public void updateBranch(T t, IPartialPageRequestHandler target)
112        {
113                final IModel<T> model = getProvider().model(t);
114                visitChildren(BranchItem.class, new IVisitor<BranchItem<T>, Void>()
115                {
116                        @Override
117                        public void component(BranchItem<T> branch, IVisit<Void> visit)
118                        {
119                                if (model.equals(branch.getModel()))
120                                {
121                                        // BranchItem always outputs its markupId
122                                        target.add(branch);
123                                        visit.stop();
124                                }
125                        }
126                });
127                model.detach();
128        }
129
130        /**
131         * Overridden to update the corresponding {@link Node} only.
132         *
133         * @param node
134         *            node to update
135         * @param target
136         *            request target must not be @code null}
137         */
138        @Override
139        public void updateNode(T node, IPartialPageRequestHandler target)
140        {
141                final IModel<T> model = getProvider().model(node);
142                visitChildren(Node.class, new IVisitor<Node<T>, Void>()
143                {
144                        @Override
145                        public void component(Node<T> node, IVisit<Void> visit)
146                        {
147                                if (model.equals(node.getModel()))
148                                {
149                                        // nodes are configured to output their markup id, see #newNodeComponent()
150                                        target.add(node);
151                                        visit.stop();
152                                }
153                                visit.dontGoDeeper();
154                        }
155                });
156                model.detach();
157        }
158
159        private class RootsModel implements IModel<T>
160        {
161                private static final long serialVersionUID = 1L;
162
163                @Override
164                public T getObject()
165                {
166                        return null;
167                }
168        }
169}