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.io.Serializable; 020import java.util.ArrayList; 021import java.util.Iterator; 022import java.util.List; 023 024import javax.swing.event.TreeModelEvent; 025import javax.swing.event.TreeModelListener; 026import javax.swing.tree.TreeModel; 027 028import org.apache.wicket.ajax.AjaxRequestTarget; 029import org.apache.wicket.extensions.markup.html.repeater.tree.AbstractTree; 030import org.apache.wicket.extensions.markup.html.repeater.tree.ITreeProvider; 031import org.apache.wicket.model.IModel; 032 033/** 034 * A provider wrapping a Swing {@link TreeModel}. 035 * <br> 036 * EXPERIMENTAL ! 037 * 038 * @author svenmeier 039 * @param <T> 040 * model object type 041 */ 042public abstract class TreeModelProvider<T> implements ITreeProvider<T> 043{ 044 045 private static final long serialVersionUID = 1L; 046 047 private final TreeModel treeModel; 048 049 private final boolean rootVisible; 050 051 protected boolean completeUpdate; 052 053 protected List<T> nodeUpdates; 054 055 protected List<T> branchUpdates; 056 057 /** 058 * Wrap the given {@link TreeModel}. 059 * 060 * @param treeModel 061 * model to wrap 062 */ 063 public TreeModelProvider(TreeModel treeModel) 064 { 065 this(treeModel, true); 066 } 067 068 /** 069 * Wrap the given {@link TreeModel}. 070 * 071 * @param treeModel 072 * the wrapped model 073 * @param rootVisible 074 * should the root be visible 075 */ 076 public TreeModelProvider(TreeModel treeModel, boolean rootVisible) 077 { 078 this.treeModel = treeModel; 079 this.rootVisible = rootVisible; 080 081 treeModel.addTreeModelListener(new Listener()); 082 } 083 084 @Override 085 public Iterator<T> getRoots() 086 { 087 if (rootVisible) 088 { 089 return new Iterator<T>() 090 { 091 boolean next = true; 092 093 @Override 094 public boolean hasNext() 095 { 096 return next; 097 } 098 099 @Override 100 public T next() 101 { 102 next = false; 103 return cast(treeModel.getRoot()); 104 } 105 106 @Override 107 public void remove() 108 { 109 throw new UnsupportedOperationException(); 110 } 111 }; 112 } 113 else 114 { 115 return getChildren(cast(treeModel.getRoot())); 116 } 117 } 118 119 @Override 120 public boolean hasChildren(T object) 121 { 122 return !treeModel.isLeaf(object); 123 } 124 125 @Override 126 public Iterator<T> getChildren(final T object) 127 { 128 return new Iterator<T>() 129 { 130 private int size = treeModel.getChildCount(object); 131 private int index = -1; 132 133 @Override 134 public boolean hasNext() 135 { 136 return index < size - 1; 137 } 138 139 @Override 140 public T next() 141 { 142 index++; 143 return cast(treeModel.getChild(object, index)); 144 } 145 146 @Override 147 public void remove() 148 { 149 throw new UnsupportedOperationException(); 150 } 151 }; 152 } 153 154 @SuppressWarnings("unchecked") 155 protected T cast(Object object) 156 { 157 return (T)object; 158 } 159 160 @Override 161 public abstract IModel<T> model(T object); 162 163 @Override 164 public void detach() 165 { 166 completeUpdate = false; 167 nodeUpdates = null; 168 branchUpdates = null; 169 } 170 171 /** 172 * Call this method after all change to the wrapped {@link TreeModel} being initiated via 173 * {@link AjaxRequestTarget}. 174 * 175 * @param tree 176 * the tree utilizing this {@link ITreeProvider} 177 * @param target 178 * the {@link AjaxRequestTarget} which initiated the changes 179 */ 180 public void update(AbstractTree<T> tree, AjaxRequestTarget target) 181 { 182 if (completeUpdate) 183 { 184 target.add(tree); 185 } 186 else 187 { 188 if (nodeUpdates != null) 189 { 190 for (T object : nodeUpdates) 191 { 192 tree.updateNode(object, target); 193 } 194 } 195 196 if (branchUpdates != null) 197 { 198 for (T object : branchUpdates) 199 { 200 tree.updateBranch(object, target); 201 } 202 } 203 } 204 205 detach(); 206 } 207 208 protected void nodeUpdate(Object[] nodes) 209 { 210 if (nodeUpdates == null) 211 { 212 nodeUpdates = new ArrayList<>(); 213 } 214 215 for (Object node : nodes) 216 { 217 nodeUpdates.add(cast(node)); 218 } 219 } 220 221 protected void branchUpdate(Object branch) 222 { 223 if (branchUpdates == null) 224 { 225 branchUpdates = new ArrayList<>(); 226 } 227 228 branchUpdates.add(cast(branch)); 229 } 230 231 private class Listener implements TreeModelListener, Serializable 232 { 233 private static final long serialVersionUID = 1L; 234 235 @Override 236 public void treeNodesChanged(TreeModelEvent e) 237 { 238 if (e.getChildIndices() == null) 239 { 240 completeUpdate = true; 241 } 242 else 243 { 244 nodeUpdate(e.getChildren()); 245 } 246 } 247 248 @Override 249 public void treeNodesInserted(TreeModelEvent e) 250 { 251 branchUpdate(e.getTreePath().getLastPathComponent()); 252 } 253 254 @Override 255 public void treeNodesRemoved(TreeModelEvent e) 256 { 257 branchUpdate(e.getTreePath().getLastPathComponent()); 258 } 259 260 @Override 261 public void treeStructureChanged(TreeModelEvent e) 262 { 263 if (e.getTreePath().getPathCount() == 1) 264 { 265 completeUpdate = true; 266 } 267 else 268 { 269 branchUpdate(e.getTreePath().getLastPathComponent()); 270 } 271 } 272 } 273}