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.util;
018
019import java.util.Collection;
020import java.util.HashSet;
021import java.util.Iterator;
022import java.util.Set;
023
024import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider;
025import org.apache.wicket.model.IDetachable;
026import org.apache.wicket.model.IModel;
027
028/**
029 * A {@link Set} implementation utilizing a {@link ITreeProvider}'s models to keep containing
030 * elements.
031 * 
032 * @author svenmeier
033 * @param <T>
034 *            type of data
035 * 
036 * @see ITreeProvider#model(Object)
037 */
038public class ProviderSubset<T> implements Set<T>, IDetachable
039{
040
041        private static final long serialVersionUID = 1L;
042
043        private ITreeProvider<T> provider;
044
045        private Set<IModel<T>> models = new HashSet<>();
046
047        /**
048         * Create an empty subset.
049         * 
050         * @param provider
051         *            the provider of the complete set
052         */
053        public ProviderSubset(ITreeProvider<T> provider)
054        {
055                this(provider, false);
056        }
057
058        /**
059         * Create a subset optionally containing all roots of the provider.
060         * 
061         * @param provider
062         *            the provider of the complete set
063         * @param addRoots
064         *            should all roots be added to this subset
065         */
066        public ProviderSubset(ITreeProvider<T> provider, boolean addRoots)
067        {
068                this.provider = provider;
069
070                if (addRoots)
071                {
072                        Iterator<? extends T> roots = provider.getRoots();
073                        while (roots.hasNext())
074                        {
075                                add(roots.next());
076                        }
077                }
078        }
079
080        @Override
081        public void detach()
082        {
083                for (IModel<T> model : models)
084                {
085                        model.detach();
086                }
087        }
088
089        @Override
090        public int size()
091        {
092                return models.size();
093        }
094
095        @Override
096        public boolean isEmpty()
097        {
098                return models.size() == 0;
099        }
100
101        @Override
102        public void clear()
103        {
104                detach();
105
106                models.clear();
107        }
108
109        @Override
110        public boolean contains(Object o)
111        {
112                IModel<T> model = model(o);
113
114                boolean contains = models.contains(model);
115
116                model.detach();
117
118                return contains;
119        }
120
121        @Override
122        public boolean add(T t)
123        {
124                return models.add(model(t));
125        }
126
127        @Override
128        public boolean remove(Object o)
129        {
130                IModel<T> model = model(o);
131
132                boolean removed = models.remove(model);
133
134                model.detach();
135
136                return removed;
137        }
138
139        @Override
140        public Iterator<T> iterator()
141        {
142                return new Iterator<T>()
143                {
144
145                        private Iterator<IModel<T>> iterator = models.iterator();
146
147                        private IModel<T> current;
148
149                        @Override
150                        public boolean hasNext()
151                        {
152                                return iterator.hasNext();
153                        }
154
155                        @Override
156                        public T next()
157                        {
158                                current = iterator.next();
159
160                                return current.getObject();
161                        }
162
163                        @Override
164                        public void remove()
165                        {
166                                iterator.remove();
167
168                                current.detach();
169                                current = null;
170                        }
171                };
172        }
173
174        @Override
175        public boolean addAll(Collection<? extends T> ts)
176        {
177                boolean changed = false;
178
179                for (T t : ts)
180                {
181                        changed |= add(t);
182                }
183
184                return changed;
185        }
186
187        @Override
188        public boolean containsAll(Collection<?> cs)
189        {
190                for (Object c : cs)
191                {
192                        if (!contains(c))
193                        {
194                                return false;
195                        }
196                }
197                return true;
198        }
199
200        @Override
201        public boolean removeAll(Collection<?> cs)
202        {
203                boolean changed = false;
204
205                for (Object c : cs)
206                {
207                        changed |= remove(c);
208                }
209
210                return changed;
211        }
212
213        @Override
214        public boolean retainAll(Collection<?> c)
215        {
216                throw new UnsupportedOperationException();
217        }
218
219        @Override
220        public Object[] toArray()
221        {
222                throw new UnsupportedOperationException();
223        }
224
225        @Override
226        public <S> S[] toArray(S[] a)
227        {
228                throw new UnsupportedOperationException();
229        }
230
231        @SuppressWarnings("unchecked")
232        private IModel<T> model(Object o)
233        {
234                return provider.model((T)o);
235        }
236
237        /**
238         * Create a model holding this set.
239         * 
240         * @return model
241         */
242        public IModel<Set<T>> createModel()
243        {
244                return new IModel<Set<T>>()
245                {
246                        private static final long serialVersionUID = 1L;
247
248                        @Override
249                        public Set<T> getObject()
250                        {
251                                return ProviderSubset.this;
252                        }
253
254                        @Override
255                        public void detach()
256                        {
257                                ProviderSubset.this.detach();
258                        }
259                };
260        }
261}