/**
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations under
* the License.
*
* The Original Code is OpenELIS code.
*
* Copyright (C) The Minnesota Department of Health. All Rights Reserved.
*/
package us.mn.state.health.lims.common.valueholder.tree;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
/**
* Class which allows users to create trees on the client browser,
* hiding all the javascript implementation details while presenting the developer
* with an easy to use javax.swing.Jtree like interface to work with.
*
* @author jalpesh
*
*/
public class Tree
{
private boolean useCookies; // in local mode, if a cookies should be used or not
private String baseDirectory; // base directory from where the images are loaded
private boolean isLocal; // whether the tree is a local tree or not
private TreeNode rootNode; // the root node of the tree
private String name; // the name of the tree
private String highlightNodePath; // Optional parameter to set highlighted nodes
private List popups; // keep track of popups of the tree. this is used when rendering the tree
private boolean instrumentation; // put in instrumentation code
/** Variables used for i18n actions */
private String textNode;
private String textCollapse;
private String textCollapsed;
private String textExpand;
private String textExpanded;
private Object textLevel;
private String textOf;
private String textSelected;
private String textNext;
private String textPrev;
private long startTime;
private int totalNodes;
/**
* Create a tree with a given name. Please note that the name of the tree should be unique in a given user session.
* So for example if your site has five trees in different pages, all the trees should have a unique name
* @param name The name of the tree. Please note that the name of the tree should not contain any of the following
* characters - single/doublequotes (',"), forward/backward slash (/,\) or the period (.)
*/
public Tree(String name)
{
this(name, null, false);
}
/**
* Create a tree with a given name. Please note that the name of the tree should be unique in a given user session.
* So for example if your site has five trees in different pages, all the trees should have a unique name
* @param name The name of the tree
* @param rootNode The root node of the tree
* @param isLocal Whether or not the tree is in local mode or a client/server mode.
*/
public Tree(String name, TreeNode rootNode, boolean isLocal)
{
this.name = name;
this.baseDirectory = "tree_images/";
this.useCookies = true;
this.isLocal = isLocal;
this.rootNode = rootNode;
this.popups = new ArrayList();
}
/**
* Each tree can optionally have one node which is highlighted. This method returns the highlighted node path for that tree.
* If this parameter is not set, upon rendering the tree will try to find a highlighted node in the TreeStateManager if it
* exists, and then use it instead.
* @return Returns the highlightNodePath.
*/
public String getHighlightNodePath()
{
return highlightNodePath;
}
/**
* Each tree can optionally have one node which is highlighted. This method sets the highlighted node path for that tree.
* If this parameter is not set, upon rendering the tree will try to find a highlighted node in the TreeStateManager if it
* exists, and then use it instead.
* @param highlightNodePath The highlightNodePath to set.
*/
public void setHighlightNodePath(String highlightNodePath)
{
this.highlightNodePath = highlightNodePath;
}
/**
* @return Returns the baseDirectory from where the tree images whill be loaded.
* This defaults to "tree_images/" which will look for the tree_images directory inside your current directory.
*/
public String getBaseDirectory()
{
return baseDirectory;
}
/**
* @param baseDirectory Set the base directory where the tree generator will look for images of the tree.
* Defaults to "tree_images/" if not set
*/
public void setBaseDirectory(String baseDirectory)
{
this.baseDirectory = baseDirectory;
}
/**
* @return Returns whether or not the tree is in a local mode or not. A tree in a local mode will not
* hit the server in case of an expand or a collapse action.
*/
public boolean isLocal()
{
return isLocal;
}
/**
* @param isLocal Sets the local mode of the tree. By default it is false.
* Local mode determines whether the entire tree is generated on the client or, the tree is generated on a need basis only.
*/
public void setLocal(boolean isLocal)
{
this.isLocal = isLocal;
}
/**
* @return Returns the name of the tree. Each tree should have a unique name to avoid conflicts.
*/
public String getName()
{
return name;
}
/**
* @param name Set the name of the tree.
*/
public void setName(String name)
{
this.name = name;
}
/**
* @return Returns the rootNode of the tree.
*/
public TreeNode getRootNode()
{
return rootNode;
}
/**
* @param rootNode Each tree should at least have one root node. This is set using this method.
*/
public void setRootNode(TreeNode rootNode)
{
this.rootNode = rootNode;
if(rootNode != null)
rootNode.setTree(this);
}
/**
* @return Returns true or false indicating whether or not the client is to use cookies if the tree
* is in a local mode (ie setLocal is true)
*/
public boolean isUseCookies()
{
return useCookies;
}
/**
* @param useCookies Indicate the tree whether or not to use cookies in case of a local mode.
*/
public void setUseCookies(boolean useCookies)
{
this.useCookies = useCookies;
}
/**
* Set the values of the displayed text messages for this tree. This is useful for localization issues.
* If you tree is only going to be used in US English local you can use the default values. However it is advisable
* to set these values if you want the tree to be fully localized.
*
* You will need to set the following values :
*
* @param textNode Default is "node"
* @param textCollapse Default is "Collapse"
* @param textCollapsed Default is "Collapsed"
* @param textExpand Default is "Expand"
* @param textExpanded Default is "Expanded"
* @param textLevel Default is "Level"
* @param textOf Default is "of"
* @param textSelected Default is "Selected"
* @param textNext Default is "Next"
* @param textPrev Default is "Prev"
*/
public void setI18nText(String textNode, String textCollapse, String textCollapsed, String textExpand, String textExpanded, String textLevel, String textOf, String textSelected, String textNext, String textPrev)
{
this.textNode = textNode;
this.textCollapse = textCollapse;
this.textCollapsed = textCollapsed;
this.textExpand = textExpand;
this.textExpanded = textExpanded;
this.textLevel = textLevel;
this.textOf = textOf;
this.textSelected = textSelected;
this.textNext = textNext;
this.textPrev = textPrev;
}
// return the javascript definition of the tree
protected String getJavascriptDefinition(HttpServletRequest request)
{
String highlightNodePath = null;
TreeStateManager treeStateManager = TreeStateManager.getInstance(request.getSession(), name);
// user has not set a highlighted node, so let us get it from the state manager
if(getHighlightNodePath() == null)
highlightNodePath = treeStateManager.getHighlightedNodePath();
// we need name, root node, base directory, isLocal, and optionally the highlight node
// root node will always be defined as _a
String script = " tree = new Tree('"+TreeNode.escapeQuotes(getName())+"',_a,'"+getBaseDirectory()+"',"+isLocal()+ (highlightNodePath != null ? ",'"+TreeNode.escapeQuotes(highlightNodePath)+"'":",null") + ");\n";
script += " tree.renderTree();\n";
return script;
}
/**
* Renders the tree.
* @param request The HttpServletRequest object
* @return The javascript representation of the tree
*/
public String renderTree(HttpServletRequest request)
{
String treeScript = "";
// mozilla does not work very well with overflow: style, especially if there is a vertical scrollbar in the page. Whenever you click
// on a link, it will jump to the middle of the div. So we are eliminating the style for every browser other than IE -->
if(!isLocal)
{
treeScript += "<form name='"+name+"ManagementForm' method='get'>\n";
treeScript += "</form>\n";
}
treeScript += "<span id='"+name+"popupPlaceholder'></span>\n";
treeScript += "<script language='javascript'>\n";
treeScript += "if(document.all) {\n";
treeScript += " document.write('<div style=\"overflow:visible; width:265px; white-space:nowrap;\">');\n";
treeScript += "} else {\n";
treeScript += " document.write('<table width=\"250px;\"><tr><td nowrap>');\n";
treeScript += "}\n";
treeScript += "{\n";
if(hasInstrumentation())
treeScript += startTimer();
// if i18n variables are initialized, let us display them
if(textNode != null)
{
treeScript += "textNode = '" + textNode + "';\n";
treeScript += "textCollapse = '" + textCollapse + "';\n";
treeScript += "textCollapsed = '" + textCollapsed + "';\n";
treeScript += "textExpand = '" + textExpand + "';\n";
treeScript += "textExpanded = '" + textExpanded + "';\n";
treeScript += "textLevel = '" + textLevel + "';\n";
treeScript += "textOf = '" + textOf + "';\n";
treeScript += "textSelected = '" + textSelected + "';\n";
treeScript += "textNext = '" + textNext + "';\n";
treeScript += "textPrev = '" + textPrev + "';\n";
}
// let us print the path delimeter for the tree nodes
treeScript += "pathDelimeter = '" + TreeNode.escapeQuotes(new String(new char[] {TreeNode.getPathDelimeter()})) + "';\n";
// generate the tree nodes first....
treeScript += generateNodeScript(rootNode, null);
// now generate the tree....
treeScript += getJavascriptDefinition(request);
treeScript += "}\n";
treeScript += "if(document.all) {\n";
treeScript += " document.write('<p> </p>');\n"; // extra space required for ie...
treeScript += " document.write('</div>');\n";
treeScript += "} else {\n";
treeScript += " document.write('</td></tr></table>');\n";
treeScript += "}\n";
if(hasInstrumentation())
treeScript += stopTimer();
treeScript += "</script>\n";
// finally generate the popups. Order of generation is important - first generate the nodes, then the tree and finally
// the popups.
for(int i=0; i<popups.size(); i++)
treeScript += ((TreePopup)popups.get(i)).getJavascriptDefinition();
return treeScript;
}
/**
* @param parent The start point of generation of the tree
* @return
*/
private String generateNodeScript(TreeNode node, String parentVarName)
{
// each node should know about the tree it belongs to. Call this before generating the script definition
node.setTree(this);
String script = node.getJavascriptDefinition(parentVarName); totalNodes++; // keep track of nodes in the system.
// create a list of popups for the tree
TreePopup p = node.getPopup();
if(p != null && !popups.contains(p)) {
p.setTreeName(getName());
popups.add(p);
}
for(int i=0; i<node.getChildren().size(); i++)
{
TreeNode n = (TreeNode)node.getChildren().get(i);
script += generateNodeScript(n, node.getVarName());
}
return script;
}
/**
* Returns whether or not the tree has instrumentation code turned on or off
* @return Returns the instrumentation.
*/
public boolean hasInstrumentation()
{
return instrumentation;
}
/**
* Sets the instrumentation for the tree to on or off. When instrumentation is turned on,
* the tree displays time taken to render the tree to the user.
* @param instrumentation The instrumentation to set.
*/
public void setInstrumentation(boolean instrumentation)
{
this.instrumentation = instrumentation;
}
/** Instrumentation code to start measuring time */
public String startTimer()
{
startTime = new Date().getTime();
return ("\tvar __sTime = new Date().getTime();\n");
}
public String stopTimer()
{
long diff = new Date().getTime() - startTime;
String s = "";
s += "\tvar __tt = new Date().getTime()-__sTime;\n";
s += "\tvar __stt = " + diff + ";\n";
s += "\tvar __total = __tt + " + diff + "\n";
s += "\tvar s0 = 'Total Nodes : ' + " + totalNodes + ";\n";
s += "\tvar s1 = 'Client Side Time Taken (ms) : ' + __tt;\n";
s += "\tvar s2 = 'Server Side Time Taken (ms) : ' + " + diff + ";\n";
s += "\tvar s3 = 'Client/Server ratio : ' + Math.round(100*__tt/__total) + '% :: ' + " + " Math.round(100*__stt/__total) + '%'; \n";
s += "\talert(s0 + '\\n' + s1 + '\\n' + s2 + '\\n' + s3);\n";
return s;
}
}