/*
* 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();
}
}