/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.portal.mop.navigation;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
/**
* <p>The context of a tree, that performs:
* <ul>
* <li>holding the list of pending changes</li>
* <li>keep a reference to the {@link NodeModel}</li>
* <li>hold a sequence for providing id for transient contexts</li>
* <li>hold the root context</li>
* </ul>
* </p>
*
* <p>The class implements the {@link Scope.Visitor} and defines a scope describing the actual content
* of the context tree.</p>
*
* @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a>
*/
class TreeContext<N> implements Scope.Visitor, NodeChangeListener<NodeContext<N>>
{
/** . */
private NodeChangeQueue<NodeContext<N>> changes;
/** . */
final NodeModel<N> model;
/** . */
boolean editMode;
/** . */
int sequence;
/** . */
final NodeContext<N> root;
TreeContext(NodeModel<N> model, NodeContext<N> root)
{
this.model = model;
this.editMode = false;
this.sequence = 0;
this.root = root;
}
public NodeChangeQueue<NodeContext<N>> getChanges()
{
return changes;
}
// Improve that method if we can
Scope.Visitor origin()
{
final Map<String, Boolean> map = new HashMap<String, Boolean>();
//
populate(map, root);
//
if (changes != null)
{
ListIterator<NodeChange<NodeContext<N>>> it = changes.listIterator(changes.size());
while (it.hasPrevious())
{
NodeChange<NodeContext<N>> change = it.previous();
if (change instanceof NodeChange.Created<?>)
{
NodeChange.Created<NodeContext<N>> created = (NodeChange.Created<NodeContext<N>>)change;
map.remove(created.target.handle);
}
else if (change instanceof NodeChange.Destroyed<?>)
{
NodeChange.Destroyed<NodeContext<N>> destroyed = (NodeChange.Destroyed<NodeContext<N>>)change;
map.put(destroyed.target.handle, Boolean.TRUE);
}
}
}
//
return new Scope.Visitor()
{
public VisitMode enter(int depth, String id, String name, NodeState state)
{
return map.containsKey(id) ? VisitMode.ALL_CHILDREN : VisitMode.NO_CHILDREN;
}
public void leave(int depth, String id, String name, NodeState state)
{
}
};
}
private void populate(Map<String, Boolean> map, NodeContext<N> ctx)
{
if (ctx.isExpanded())
{
map.put(ctx.handle, Boolean.TRUE);
for (NodeContext<N> current = ctx.getFirst();current != null;current = current.getNext())
{
populate(map, current);
}
}
}
void addChange(NodeChange<NodeContext<N>> change)
{
if (editMode)
{
throw new AssertionError();
}
if (changes == null)
{
changes = new NodeChangeQueue<NodeContext<N>>();
}
//
if (change.target.tree != this)
{
// Normally should be done for all arguments depending on the change type
throw new AssertionError("Ensure we are not mixing badly things");
}
// Perform state modification here
if (change instanceof NodeChange.Renamed<?>)
{
NodeChange.Renamed<NodeContext<N>> renamed = (NodeChange.Renamed<NodeContext<N>>)change;
renamed.target.name = renamed.name;
}
else if (change instanceof NodeChange.Created<?>)
{
NodeChange.Created<NodeContext<N>> added = (NodeChange.Created<NodeContext<N>>)change;
if (added.previous != null)
{
added.previous.insertAfter(added.target);
}
else
{
added.parent.insertAt(0, added.target);
}
}
else if (change instanceof NodeChange.Moved<?>)
{
NodeChange.Moved<NodeContext<N>> moved = (NodeChange.Moved<NodeContext<N>>)change;
if (moved.previous != null)
{
moved.previous.insertAfter(moved.target);
}
else
{
moved.to.insertAt(0, moved.target);
}
}
else if (change instanceof NodeChange.Destroyed<?>)
{
NodeChange.Destroyed<NodeContext<N>> removed = (NodeChange.Destroyed<NodeContext<N>>)change;
removed.target.remove();
}
else if (change instanceof NodeChange.Updated<?>)
{
NodeChange.Updated<NodeContext<N>> updated = (NodeChange.Updated<NodeContext<N>>)change;
updated.target.state = updated.state;
}
//
changes.addLast(change);
}
boolean hasChanges() {
return changes != null && changes.size() > 0;
}
List<NodeChange<NodeContext<N>>> peekChanges()
{
if (hasChanges())
{
return changes;
}
else
{
return Collections.emptyList();
}
}
List<NodeChange<NodeContext<N>>> popChanges()
{
if (hasChanges())
{
LinkedList<NodeChange<NodeContext<N>>> tmp = changes;
changes = null;
return tmp;
}
else
{
return Collections.emptyList();
}
}
NodeContext<N> getNode(String handle)
{
return root.getDescendant(handle);
}
NodeContext<N> create(String handle, String name, NodeState state)
{
return new NodeContext<N>(this, handle, name, state, true);
}
// Scope.Visitor implementation -------------------------------------------------------------------------------------
public VisitMode enter(int depth, String id, String name, NodeState state)
{
NodeContext<N> descendant = root.getDescendant(id);
if (descendant != null)
{
return descendant.isExpanded() ? VisitMode.ALL_CHILDREN : VisitMode.NO_CHILDREN;
}
else
{
return VisitMode.NO_CHILDREN;
}
}
public void leave(int depth, String id, String name, NodeState state)
{
}
//
public void onCreate(NodeContext<N> target, NodeContext<N> parent, NodeContext<N> previous, String name) throws NavigationServiceException
{
addChange(new NodeChange.Created<NodeContext<N>>(parent, previous, target, name));
}
public void onDestroy(NodeContext<N> target, NodeContext<N> parent)
{
addChange(new NodeChange.Destroyed<NodeContext<N>>(parent, target));
}
public void onRename(NodeContext<N> target, NodeContext<N> parent, String name) throws NavigationServiceException
{
addChange(new NodeChange.Renamed<NodeContext<N>>(parent, target, name));
}
public void onUpdate(NodeContext<N> target, NodeState state) throws NavigationServiceException
{
addChange(new NodeChange.Updated<NodeContext<N>>(target, state));
}
public void onMove(NodeContext<N> target, NodeContext<N> from, NodeContext<N> to, NodeContext<N> previous) throws NavigationServiceException
{
addChange(new NodeChange.Moved<NodeContext<N>>(from, to, previous, target));
}
public void onAdd(NodeContext<N> target, NodeContext<N> parent, NodeContext<N> previous)
{
throw new UnsupportedOperationException();
}
public void onRemove(NodeContext<N> target, NodeContext<N> parent)
{
throw new UnsupportedOperationException();
}
}