/*
 * $Id: TreeView.java,v 1.47 2006/10/12 11:37:18 marbobo Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: TreeView.java,v $
 * Revision 1.47  2006/10/12 11:37:18  marbobo
 * + kleine Design-Anpassung
 *
 * Revision 1.46  2006/10/12 11:01:26  marbobo
 * + Bei gesperrten Ordnern Namen anzeigen
 *
 * Revision 1.45  2006/10/04 22:41:14  marbobo
 * + kleine Performancesteigerung
 *
 * Revision 1.44  2006/10/04 14:16:31  marbobo
 * + kleiner Bugfix
 *
 * Revision 1.43  2006/10/02 14:39:12  marbobo
 * + Eigenes Schlüssel für den Explorer in den Suchmasken eingefügt
 *
 * Revision 1.42  2006/10/02 00:25:53  marbobo
 * + Explorer Fehlerbeseitigt (Hoffe endgültig)
 *
 * Revision 1.41  2006/09/30 22:40:57  marbobo
 * + Explorer Fehlerbeseitigt
 * + Explorer kann nun auch alle Kategorien anzeigen in denen man Leserechte hat
 *
 * Revision 1.40  2006/05/09 15:43:50  marbobo
 * + Kategorie- oder Dokumentnamen unverkürzt anzeigen
 *
 * Revision 1.39  2005/10/12 15:18:03  lawern
 * Es geht auch ohne NumberFormatException.
 *
 * Revision 1.38  2005/10/12 15:08:02  lawern
 * NumberFormatException abgefangen
 *
 * Revision 1.37  2005/09/05 22:46:45  marbobo
 * + Anpassungen bzgl. Trash-Funktionalität
 *
 * Revision 1.36  2005/06/28 00:02:09  marbobo
 * + Darstellungsprobleme mit Nahost-Schriftarten korrigiert
 *
 * Revision 1.35  2005/06/23 01:04:08  aram
 * + Anpassungen bzgl. Cluster-Moduls
 *
 * Revision 1.34  2005/05/10 12:56:34  marbobo
 * + Anpassung bei TreeView (Auswahl der aktuellen Kategorie)
 *
 * Revision 1.33  2005/04/28 16:25:55  marbobo
 * + Aussehen angepasst
 *
 * Revision 1.32  2005/04/26 00:27:45  marbobo
 * + Ausgewählte Kategorie markieren
 *
 * Revision 1.31  2005/04/25 14:46:24  marbobo
 * - onfocus rausgenommen und draw() synchrozed gemacht
 *
 * Revision 1.30  2005/04/21 13:14:37  marbobo
 * + beidseitige Navigation zwischen Dokumentablage und Explorer
 *
 * Revision 1.29  2005/02/04 19:28:40  marbobo
 * + Verwendung einer Unicode-Sensitiven-Filtermethode
 *
 * Revision 1.28  2005/01/06 16:00:40  jonyp
 * consider flag "ListAccessibleCategoriesOnly" in drawChild
 *
 * Revision 1.1  2003/04/29 11:08:25  koppy
 * Initiale Version
 *
 */
package vkc.util.treeview;

import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspWriter;

import vkc.db.entities.VKCUser;
import vkc.db.serializer.VKCAclSerializer;
import vkc.exception.DBException;
import vkc.misc.VKCLogManager;
import vkc.security.PermissionManager;
import vkc.security.VKCAcl;
import vkc.util.VKCStringUtils;
import vkc.view.WebShare;

/**
 * This class represents a treeview
 *
 * @author koppy
 * @see TreeViewItem
 */
public final class TreeView implements Serializable {
	public static final String CLUSTER_TREE_KEY = "bean.cluster.treeview";
	public static final String SEARCH_TREE_KEY = "bean.search.treeview";
	public static final String TREE_KEY = "bean.treeview";

	private boolean checkPermission = true;
	private boolean drawCheckBox = false;
	private boolean expand;
	private boolean showRoot = false;

	/** Manager flags */
	private long lastModified = System.currentTimeMillis();

	/**
	 * Map of values of type TreeViewItem
	 * KEY => VALUE: TreeViewItem.getName() => TreeViewItem
	 */
	private HashMap treeViewItems = new HashMap();

	private HashSet expNodes = new HashSet();
	private HashSet newExpNodes = new HashSet();

	private String activeNode = "";
	private String imageDir = "images";
	private String linkPath = null;
	private String nodes;
	private String queryString = "";
	private String path = "/treeview.do";
	private String selNode = "";
	private String target = "_blank";
	private String varId = null;

	/**
	 * Default Rootnode
	 */
	private TreeViewItem rootNode = new TreeViewItem("ROOT", null, "Root", null);

	private VKCUser user = null;

	/**
	 * Constructor
	 */
	public TreeView() {
	}

	/**
	 * Constructor
	 *
	 * @param target name of the target frame
	 * @param imageDir name of the image directory
	 */
	public TreeView(String target, String imageDir) {
		this.target = target;
		this.imageDir = imageDir;
	}

	/**
	 * Adds a child to the rootnode of this tree
	 *
	 * @param child the child to be added
	 */
	public void addChild(TreeViewItem child) {
		this.rootNode.addChild(child);
	}

	/**
	 * 
	 * @param child
	 */
	private void buildChildArray(TreeViewItem child) {
		treeViewItems.put(child.getId(), child);

		for (Iterator it = child.getChildren().iterator(); it.hasNext();) {
			TreeViewItem item = (TreeViewItem) it.next();
			this.buildChildArray(item);
		}
	}

	public TreeViewItem getTreeViewItemById(String id) {
		if (this.treeViewItems.isEmpty()) {
			this.buildChildArray(rootNode);
		}

		return (TreeViewItem) this.treeViewItems.get(id);
	}

	/**
	 * Draws the decoration(+/-, etc.) of a Treenode
	 *
	 * @param child
	 * @param out
	 * @throws IOException
	 */
	private void drawDecoration(TreeViewItem child, JspWriter out) throws IOException {
		TreeViewItem parent = child.getParent();

		if (parent != null && this.showRoot) {
			out.print("<img src=\"" + this.imageDir + "/spacer.gif\" style=\"vertical-align:middle\" width=\"16\" height=\"20\" border=\"0\"/>");
		}

		List deco = new ArrayList();

		for (TreeViewItem c = parent; c != null && c.getParent() != null; c = c.getParent()) {
			if (c.getParent().getVirtualChildSize() - 1 > c.getPos()) {
				deco.add("<img src=\"" + this.imageDir + "/dotsl.gif\" style=\"vertical-align:middle\" width=\"16\" height=\"20\" border=\"0\"/>");
			} else {
				deco.add("<img src=\"" + this.imageDir + "/spacer.gif\" style=\"vertical-align:middle\" width=\"16\" height=\"20\" border=\"0\"/>");
			}
		}

		// draw the deco backwards
		for (int i = deco.size() - 1; i >= 0; i--) {
			out.print(deco.get(i));
		}

		String img;
		boolean writeLink = true;

		if (!child.isLeaf()) {
			//display -/+
			if (child.isExpanded()) {
				//minusdots/b
				if (parent == null || child.getPos() == parent.getVirtualChildSize() - 1) {
					// last item => minusdotsb
					img = this.imageDir + "/minusdotsb.gif";
				} else {
					// minusdots
					img = this.imageDir + "/minusdots.gif";
				}
			} else {
				//plusdots
				if (parent == null || child.getPos() == parent.getVirtualChildSize() - 1) {
					// last item => plusdotsb
					img = this.imageDir + "/plusdotsb.gif";
				} else {
					// plusdots
					img = this.imageDir + "/plusdots.gif";
				}
			}
		} else {
			if (parent == null || child.getPos() == parent.getVirtualChildSize() - 1) {
				// last item => dotsb
				img = this.imageDir + "/dotsb.gif";
			} else {
				// plusdots
				img = this.imageDir + "/dots.gif";
			}

			writeLink = false;
		}

		StringBuffer b = new StringBuffer();

		if (writeLink) {
			b.append("<a href=\"");
			b.append(path);
			b.append("?Nodes=");
			b.append(this.nodes);
			b.append("&selNode=");
			b.append(child.getId());
			b.append("&activeNode=");
			b.append(this.activeNode);
			b.append("&expand=");
			b.append(!child.isExpanded());

			if (this.varId != null) {
				b.append("&varId=").append(varId);
			}

			b.append("#" + child.getId() + "\" >");
			//b.append( "onfocus=\"this.blur();\">" );
		}

		b.append("<img src=\"");
		b.append(img);
		b.append("\" style=\"vertical-align:middle\" width=\"16\" height=\"20\" border=\"0\">");

		if (writeLink) {
			b.append("</a>");
		}

		out.print(b.toString());
	}

	/**
	 *
	 * @param child
	 * @param out
	 */
	private void drawIcon(TreeViewItem child, JspWriter out) throws IOException {
		String img;

		if (child.getImage() == null) {
			if (!child.isLeaf()) {
				if (child.isExpanded()) {
					img = this.imageDir + "/folder-open.gif";
				} else {
					img = this.imageDir + "/folder-closed.gif";
				}
			} else {
				img = this.imageDir + "/folder-closed.gif";
			}
		} else {
			img = this.imageDir + "/" + child.getImage();
		}

		out.print("<img src=\"" + img + "\" style=\"vertical-align:middle\" border=\"0\">");
	}

	/**
	 *
	 * @param child
	 * @param out
	 */
	private void drawCheckBox(TreeViewItem child, JspWriter out) throws IOException {
		if (!child.isLeaf() && child.isDrawCheckBox()) {
			String checked = child.isChecked() ? "checked=\"checked\"" : "";
			String params = "&Nodes=" + this.nodes;
			params += "&selNode=" + this.selNode;
			params += "&activeNode=" + this.activeNode;
			params += "&expand=" + (this.expand ? "true" : "false");
			params += "#" + this.selNode;
			String onClick = "onClick=\"javascript:toggleTreeItem(" + child.getId() + ",'" + params + "')\"";
			out.print("<input type=\"checkbox\" name=\"treeItem("
					+ child.getId() + ").checked\" value=\""
					+ child.isChecked() + "\" " + checked + " "
					+ onClick + ">");
		}
	}

	/**
	 * Draws the tree
	 *
	 * @param request the incoming HTTP request
	 * @param out JspWriter
	 * @throws IOException
	 */
	public synchronized void draw(HttpServletRequest request, JspWriter out) throws IOException {
		this.queryString = request.getQueryString();

		// parse url and configure the expansion/checkboxes of the tree nodes
		this.nodes = request.getParameter("Nodes");
		this.selNode = request.getParameter("selNode");
		this.activeNode = request.getParameter("activeNode");
		this.varId = request.getParameter("varId");

		String expandString = request.getParameter("expand");
		this.expand = expandString != null && expandString.trim().equals("true");

		this.newExpNodes.clear();
		this.expNodes.clear();

		boolean tidyUpExpNodes = false;

		if (this.treeViewItems.isEmpty()) {
			this.buildChildArray(this.rootNode);
			tidyUpExpNodes = true;
		}

		if (this.expand) {
			if (this.nodes == null) {
				this.nodes = this.selNode;
			} else {
				this.nodes += "|" + this.selNode;
			}
		}

		this.expNodes = this.toSet(nodes, tidyUpExpNodes);

		// parse/configure expansion of tree nodes
		for (Iterator it = expNodes.iterator(); it.hasNext();) {
			String n = (String) it.next();

			if (n.equals(this.selNode) && this.expand == false) {
				continue;
			}

			TreeViewItem item = this.getTreeViewItemById(n);

			if (item == null) {
				continue;
			}

/*
			observeItem(item);
			if (item.isLocked()) {
				continue;
			}
*/
			item.setExpanded(true);
			this.newExpNodes.add(n);
		}

		//alle Knoten oberhalb des selektierten Knoten expandieren
		TreeViewItem item = this.getTreeViewItemById(this.selNode);

		if (item != null) {
			for (TreeViewItem parent = item.getParent(); parent != null; parent = parent.getParent()) {
				parent.setExpanded(true);
				this.newExpNodes.add(parent.getId());
			}
		}

		this.nodes = VKCStringUtils.vectorToString(new Vector(this.newExpNodes), "|");

		draw(out);
		reset();
	}

	/**
	 * Updates the locked-State of the given treeview item 
	 * according to the permissions
	 * @param item
	 */
	private void observeItem(TreeViewItem item) {
		if (isCheckPermission()) {
			if (item.isObserved()) {
				return;
			}

			item.setLocked(!this.hasPermission(item));
		}
	}

	/**
	 * Resets the state of the modified nodes
	 *
	 */
	private void reset() {
		for (Iterator it = this.newExpNodes.iterator(); it.hasNext();) {
			String n = (String) it.next();
			((TreeViewItem) treeViewItems.get(n)).setExpanded(false);
			it.remove();
		}
	}

	/**
	 * Draws the tree to the JspWriter
	 *
	 * @param out
	 * @throws IOException
	 */
	private void draw(JspWriter out) throws IOException {
		if (this.showRoot) {
			out.print("<span dir=\"ltr\">");
			resetChilds(rootNode);
			prepareDrawChild(rootNode);
			drawChild(rootNode, out);
			out.print("</span>");
		} else {
			out.print("<span dir=\"ltr\">");

			for (Iterator it = rootNode.getChildren().iterator(); it.hasNext();) {
				TreeViewItem item = (TreeViewItem) it.next();
				resetChilds(item);
				prepareDrawChild(item);
				drawChild(item, out);
			}

			out.print("</span>");
		}
	}

	private void drawChild(TreeViewItem child, JspWriter out) throws IOException {
		if (child.isDraw()) {
			this.drawDecoration(child, out);
			this.drawIcon(child, out);

			if (this.isDrawCheckBox()) {
				this.drawCheckBox(child, out);
			}
			
			this.drawLink(child, out);

			if (child.isExpanded()) {
				for (Iterator it = child.getChildren().iterator(); it.hasNext();) {
					TreeViewItem item = (TreeViewItem) it.next();
					this.drawChild(item, out);
				}
			}
		}
	}

	private void drawLink(TreeViewItem child, JspWriter out) throws IOException {
		int listAccessibleCatsOnly = Integer.parseInt(WebShare.getSysParameterValue("CATEGORYVIEW", "ListAccessibleCategoriesOnly", "0"));
		StringBuffer b = new StringBuffer();

		if (child.getLink() != null && !(listAccessibleCatsOnly != 0 && child.isLocked())) {
			b.append("&#160;<a name=\"");
			b.append(child.getId());
			b.append("\" href=\"");
			b.append(child.getLink());
			b.append("\" title=\"");
			b.append(VKCStringUtils.filterUnicodeChars(child.getText()));

			if (this.activeNode != null && child.getId().equals(this.activeNode)) {
				b.append("\" style=\"color:white;font-weight:normal;font-style:normal;background-color:#0066cc;vertical-align:middle;");
			} else {
				b.append("\" style=\"vertical-align:middle;");
			}

			b.append("\" target=\"");

			if (child.getTarget() != null) {
				b.append(child.getTarget());
			} else {
				b.append(this.target);
			}

			b.append("\">");
			b.append(VKCStringUtils.filterUnicodeChars(child.getText()));
			b.append("</a><br />");
		} else {
			if (this.linkPath != null && !(listAccessibleCatsOnly != 0 && child.isLocked())) {
				if (this.linkPath.startsWith("javascript:")) {
					b.append("&#160;<a name=\"");
					b.append(child.getId());
					b.append("\" href=\"");
					b.append(replaceVars(this.linkPath, child.getId()));
					b.append("\" title=\"");
					b.append(VKCStringUtils.filterUnicodeChars(child.getText()));

					if (this.activeNode != null && child.getId().equals(this.activeNode)) {
						b.append("\" style=\"color:white;font-weight:normal;font-style:normal;background-color:#0066ccvertical-align:middle;");
					} else {
						b.append("\" style=\"vertical-align:middle;");
					}

					b.append("\">");
					b.append(VKCStringUtils.filterUnicodeChars(child.getText()));
					b.append("</a><br />");
				} else {
					b.append("&#160;<a name=\"");
					b.append(child.getId());
					b.append("\" href=\"");
					b.append(this.linkPath);
					b.append(child.getId());
					b.append("\" title=\"");
					b.append(VKCStringUtils.filterUnicodeChars(child.getText()));

					if (this.activeNode != null && child.getId().equals(this.activeNode)) {
						b.append("\" style=\"color:white;font-weight:normal;font-style:normal;background-color:#0066cc;vertical-align:middle;");
					} else {
						b.append("\" style=\"vertical-align:middle;");
					}

					b.append("\" target=\"");

					if (child.getTarget() != null) {
						b.append(child.getTarget());
					} else {
						b.append(this.target);
					}

					b.append("\">");
					b.append(VKCStringUtils.filterUnicodeChars(child.getText()));
					b.append("</a><br />");
				}
			} else {
				b.append("&#160;<a name=\"");
				b.append(child.getId());
				b.append("\" style=\"text-decoration:none;vertical-align:middle;\">");
				b.append(VKCStringUtils.filterUnicodeChars(child.getText()));
				b.append("</a><br />");
			}
		}

		out.print("<span dir=\"ltr\">");
		out.print(b.toString());
		out.print("</span>");
	}

	private void prepareDrawChild(TreeViewItem child) throws IOException {
		this.observeItem(child);

		// check flag
		int listAccessibleCatsOnly = Integer.parseInt(WebShare.getSysParameterValue("CATEGORYVIEW", "ListAccessibleCategoriesOnly", "0"));

		// do not display child, if child is locked (means forbidden access) 
		// and accessible categories should only be displayed
		if (listAccessibleCatsOnly != 0 && child.isLocked()) {
			if (child.isLeaf() || !hasReadableChild(child)) {
				correctParentChildPosition(child);
				child.setDraw(false);
				
				return;
			}
		}

		if (WebShare.isTrashEnabled() && WebShare.getTrashCategory().getCategoryId().toString().equals(child.getId())) {
			correctParentChildPosition(child);
			child.setDraw(false);

			return;
		}

		if (child.isExpanded()) {
			for (Iterator it = child.getChildren().iterator(); it.hasNext();) {
				TreeViewItem item = (TreeViewItem) it.next();
				this.prepareDrawChild(item);
			}
		}
	}

	private void correctParentChildPosition(TreeViewItem child) {
		if (child != null && child.getParent() != null) {
			for (Iterator iter = child.getParent().getChildren().iterator(); iter.hasNext();) {
				TreeViewItem c = (TreeViewItem) iter.next();
				
				if (c.getPos() > child.getPos()) {
					c.setPos(c.getPos() - 1);
				}
			}
			
			child.getParent().setVirtualChildSize(child.getParent().getVirtualChildSize() - 1);
		}
	}

	private boolean hasReadableChild(TreeViewItem item) {
		for (Iterator iter = item.getChildren().iterator(); iter.hasNext();) {
			TreeViewItem child = (TreeViewItem) iter.next();
			
			observeItem(child);
			
			if (!child.isLocked()) {
				return true;
			}
			
			if (hasReadableChild(child)) {
				return true;
			}
		}
		
		return false;
	}

	private void resetChilds(TreeViewItem node) {
		if (node != null) {
			node.setVirtualChildSize(node.getChildren().size());
			node.setDraw(true);
			int i = 0;
			
			for (Iterator iter = node.getChildren().iterator(); iter.hasNext();) {
				TreeViewItem child = (TreeViewItem) iter.next();
				resetChilds(child);
				child.setPos(i++);
			}
		}
	}

	private String replaceVars(String input, String replacement) {
		if (input == null || input.length() == 0 || replacement == null || replacement.length() == 0) {
			return input;
		}

		if (input.indexOf("${") < 0) {
			return input; // nothing to replace
		}

		StringBuffer newInput = new StringBuffer();

		Pattern p = Pattern.compile("\\$\\{(.)+\\}");
		Matcher m = p.matcher(input);

		try {
			while (m.find()) {
				String property = m.group();
				property = property.substring(2, property.length() - 1);

				m.appendReplacement(newInput, replacement);
			}
		} catch (Exception e) {
			VKCLogManager.errorLog(this, "", e.getMessage(), e);

			return input;
		}

		m.appendTail(newInput);

		if (varId != null) {
			newInput.deleteCharAt(newInput.length() - 1);
			newInput.deleteCharAt(newInput.length() - 1);
			newInput.append(", '" + varId + "');");
		}

		return newInput.toString();
	}

	/**
	 * Returns the '|' separated nodelist as a set
	 *  
	 * @param nodes '|' separated string
	 * @param tidyUp if set to true every id in the nodelist
	 * is checked whether it is still available or has been deleted
	 * @return
	 */
	private HashSet toSet(String nodes, boolean tidyUp) {
		if (nodes != null) {
			this.expNodes = new HashSet(VKCStringUtils.stringToVector(nodes, "|"));

			if (tidyUp) {
				for (Iterator it = expNodes.iterator(); it.hasNext();) {
					TreeViewItem item = this.getTreeViewItemById(it.next().toString());

					if (item == null || item.isLeaf()) {
						it.remove();
					}
				}
			}

			return this.expNodes;
		}

		if (this.selNode == null) {
			return this.expNodes;
		}

		// selNode is set, but without any expanded nodes
		TreeViewItem item = this.getTreeViewItemById(this.selNode);

		if (item == null) {
			return this.expNodes;
		}

		for (TreeViewItem parent = item.getParent(); parent != null; parent = parent.getParent()) {
			this.expNodes.add(parent.getId());
		}

		return this.expNodes;
	}

	/**
	 * Checks whether the given item has
	 * sufficient permissions
	 * 
	 * @param item
	 * @return
	 */
	private boolean hasPermission(TreeViewItem item) {
		try {
			VKCAcl acl = VKCAclSerializer.getAclByCategoryId(new Integer(item.getId()));

			return PermissionManager.hasPermission(acl, user, 'r');
		} catch (NumberFormatException e) {
			VKCLogManager.errorLog(this, "3", "Checking rights: NumberFormatException! Returning false!", e);

			return false;
		} catch (DBException e) {
			VKCLogManager.errorLog(this, "3", "Checking rights: DB-Error! Returning false!", e);

			return false;
		}
	}

	public String getImageDir() {
		return imageDir;
	}

	public TreeViewItem getRootNode() {
		return rootNode;
	}

	public boolean isShowRoot() {
		return showRoot;
	}

	public String getTarget() {
		return target;
	}

	public void setImageDir(String string) {
		imageDir = string;
	}

	public void setRootNode(TreeViewItem item) {
		rootNode = item;
	}

	public void setShowRoot(boolean b) {
		showRoot = b;
	}

	public void setTarget(String string) {
		target = string;
	}

	public String getPath() {
		return path;
	}

	public void setPath(String path) {
		this.path = path;
	}

	/**
	 * Checks whether this tree structure is up to date or not
	 * @return
	 */
	public boolean isUpToDate() {
		return this.lastModified >= TreeViewManager.getInstance().getGlobalTreeView().getLastModified();
	}

	public long getLastModified() {
		return lastModified;
	}

	public void setLastModified(long l) {
		lastModified = l;
	}

	public String getLinkPath() {
		return linkPath;
	}

	public void setLinkPath(String string) {
		linkPath = string;
	}

	public void removeTreeViewItem(String itemId) {
		this.treeViewItems.remove(itemId);
	}

	public void addTreeViewItem(TreeViewItem item) {
		this.treeViewItems.put(item.getId(), item);
	}

	public VKCUser getUser() {
		return user;
	}

	public void setUser(VKCUser user) {
		this.user = user;
	}

	public String getQueryString() {
		return queryString;
	}

	public void setQueryString(String string) {
		queryString = string;
	}

	/**
	 * Clears the list of treeview items 
	 */
	public void clearTreeViewItems() {
		this.treeViewItems.clear();
	}

	public String getNodeString() {
		return nodes;
	}

	public boolean isCheckPermission() {
		return checkPermission;
	}

	public void setCheckPermission(boolean checkPermission) {
		this.checkPermission = checkPermission;
	}

	public boolean isDrawCheckBox() {
		return drawCheckBox;
	}

	public void setDrawCheckBox(boolean drawCheckBox) {
		this.drawCheckBox = drawCheckBox;
	}

	public String getActiveNode() {
		return activeNode;
	}

	public void setActiveNode(String activeNode) {
		this.activeNode = activeNode;
	}

	public boolean isExpand() {
		return expand;
	}

	public void setExpand(boolean expand) {
		this.expand = expand;
	}

	public String getNodes() {
		return nodes;
	}

	public void setNodes(String nodes) {
		this.nodes = nodes;
	}

	public String getSelNode() {
		return selNode;
	}

	public void setSelNode(String selNode) {
		this.selNode = selNode;
	}

	public String getClearQueryString() {
		String params = "Nodes=" + this.nodes;
		params += "&selNode=" + this.selNode;
		params += "&activeNode=" + this.activeNode;
		params += "&expand=" + (this.expand ? "true" : "false");
		params += "#" + this.selNode;

		return params;
	}
}