/*
* 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.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}.
* <br>
* 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());
}
@Override
public Iterator<T> getRoots()
{
if (rootVisible)
{
return new Iterator<T>()
{
boolean next = true;
@Override
public boolean hasNext()
{
return next;
}
@Override
public T next()
{
next = false;
return cast(treeModel.getRoot());
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
else
{
return getChildren(cast(treeModel.getRoot()));
}
}
@Override
public boolean hasChildren(T object)
{
return !treeModel.isLeaf(object);
}
@Override
public Iterator<T> getChildren(final T object)
{
return new Iterator<T>()
{
private int size = treeModel.getChildCount(object);
private int index = -1;
@Override
public boolean hasNext()
{
return index < size - 1;
}
@Override
public T next()
{
index++;
return cast(treeModel.getChild(object, index));
}
@Override
public void remove()
{
throw new UnsupportedOperationException();
}
};
}
@SuppressWarnings("unchecked")
protected T cast(Object object)
{
return (T)object;
}
@Override
public abstract IModel<T> model(T object);
@Override
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<>();
}
for (Object node : nodes)
{
nodeUpdates.add(cast(node));
}
}
protected void branchUpdate(Object branch)
{
if (branchUpdates == null)
{
branchUpdates = new ArrayList<>();
}
branchUpdates.add(cast(branch));
}
private class Listener implements TreeModelListener, Serializable
{
private static final long serialVersionUID = 1L;
@Override
public void treeNodesChanged(TreeModelEvent e)
{
if (e.getChildIndices() == null)
{
completeUpdate = true;
}
else
{
nodeUpdate(e.getChildren());
}
}
@Override
public void treeNodesInserted(TreeModelEvent e)
{
branchUpdate(e.getTreePath().getLastPathComponent());
}
@Override
public void treeNodesRemoved(TreeModelEvent e)
{
branchUpdate(e.getTreePath().getLastPathComponent());
}
@Override
public void treeStructureChanged(TreeModelEvent e)
{
if (e.getTreePath().getPathCount() == 1)
{
completeUpdate = true;
}
else
{
branchUpdate(e.getTreePath().getLastPathComponent());
}
}
}
}