0001: //** Copyright Statement ***************************************************
0002: //The Salmon Open Framework for Internet Applications (SOFIA)
0003: // Copyright (C) 1999 - 2002, Salmon LLC
0004: //
0005: // This program is free software; you can redistribute it and/or
0006: // modify it under the terms of the GNU General Public License version 2
0007: // as published by the Free Software Foundation;
0008: //
0009: // This program is distributed in the hope that it will be useful,
0010: // but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0012: // GNU General Public License for more details.
0013: //
0014: // You should have received a copy of the GNU General Public License
0015: // along with this program; if not, write to the Free Software
0016: // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
0017: //
0018: // For more information please visit http://www.salmonllc.com
0019: //** End Copyright Statement ***************************************************
0020: package com.salmonllc.html.treeControl;
0021:
0022: /////////////////////////
0023: //$Archive: /SOFIA/SourceCode/com/salmonllc/html/treeControl/TreeBuffer.java $
0024: //$Author: Len $
0025: //$Revision: 18 $
0026: //$Modtime: 2/20/04 8:22a $
0027: /////////////////////////
0028:
0029: import java.io.PrintWriter;
0030: import java.sql.Timestamp;
0031: import java.util.Enumeration;
0032: import java.util.Stack;
0033: import java.util.Vector;
0034:
0035: import com.salmonllc.html.HtmlComponent;
0036: import com.salmonllc.html.HtmlContainer;
0037: import com.salmonllc.html.HtmlFormComponent;
0038: import com.salmonllc.html.HtmlText;
0039: import com.salmonllc.sql.DataStoreBuffer;
0040: import com.salmonllc.sql.DataStoreEvaluator;
0041: import com.salmonllc.sql.DataStoreException;
0042: import com.salmonllc.util.MessageLog;
0043: import com.salmonllc.util.VectorSort;
0044:
0045: /**
0046: * This class stores the internal state of an HtmlTreeControl.
0047: */
0048:
0049: public class TreeBuffer implements java.io.Serializable {
0050: public static final int SELECT_MANY = 2;
0051: public static final int SELECT_NONE = 0;
0052: public static final int SELECT_ONE = 1;
0053:
0054: public static final int MODE_SUBMIT = 0;
0055: public static final int MODE_LINK = 1;
0056:
0057: public static final int SORT_ASC = DataStoreBuffer.SORT_ASC;
0058: public static final int SORT_DES = DataStoreBuffer.SORT_DES;
0059: public static final int SORT_ANY = -1;
0060:
0061: TreeNode _root, _current;
0062: Vector _list = new Vector();
0063: Vector _images = new Vector();
0064: TreeTraversalCallBack _ttcb;
0065: Integer _nextListItem = new Integer(-1);
0066: boolean _showRoot = true;
0067: int _scrollTo = -1;
0068: int _selected = -1;
0069: int _count = 0;
0070: int _selectMode = SELECT_NONE;
0071: int _mode = MODE_SUBMIT;
0072: Vector _headers;
0073: Vector _nodeComponents;
0074:
0075: /**
0076: * This method places an image after the text of the current tree node.
0077: * @param imageNo The number of the image to use (images are set using the addImage) method.
0078: * @param text The tooltip for the image.
0079: * @param url The url to go to when the user clicks on this image.
0080: */
0081:
0082: public int addAdditionalImage(int imageNo, String text, String url) {
0083: if (_current == null)
0084: return -1;
0085:
0086: return _current.addAdditionalImage(imageNo, text, url);
0087: }
0088:
0089: /**
0090: * This method adds an item to the tree under the current item.
0091: * @param text The text to show on the line
0092: * @param URL The URL to go to if the user clicks on the text.
0093: * @param image The number of the image to use if the item is contracted (images are set using the addImage) method.
0094: * @param image The number of the image to use if the item is expanded (images are set using the addImage) method.
0095: * @param imageURL The url to go to when the user clicks on the image.
0096: * @param key An object attached to the tree node containing any information that needs to be attached to this node.
0097: */
0098:
0099: public int addChild(String text, String URL, int image,
0100: int expImage, String imageURL, Object key) {
0101: TreeNode n = new TreeNode(image, expImage, text, URL, imageURL,
0102: key);
0103: return addChild(n);
0104: }
0105:
0106: private int addChild(TreeNode n) {
0107: int retval;
0108:
0109: if (_nextListItem.intValue() == -1) {
0110: _list.addElement(n);
0111: retval = _list.size() - 1;
0112: } else {
0113: Integer bucket = (Integer) _list.elementAt(_nextListItem
0114: .intValue());
0115: _list.setElementAt(n, _nextListItem.intValue());
0116: retval = _nextListItem.intValue();
0117: _nextListItem = new Integer(bucket.intValue());
0118: }
0119:
0120: n.setHandle(retval);
0121:
0122: if (_root == null) {
0123: _root = n;
0124: _current = n;
0125: n.setExpanded(true);
0126: n.setLevel(0);
0127: } else {
0128: n.setParent(_current);
0129: TreeNode firstChild = _current.getFirstChild();
0130: TreeNode lastChild = _current.getLastChild();
0131: n.setLevel(_current.getLevel() + 1);
0132: n.setPriorNode(lastChild);
0133: if (firstChild == null) {
0134: _current.setFirstChild(n);
0135: _current.setLastChild(n);
0136: } else {
0137: _current.setLastChild(n);
0138: lastChild.setNextNode(n);
0139: }
0140:
0141: }
0142:
0143: return retval;
0144: }
0145:
0146: /**
0147: * This method adds an image to the list of images used by the tree.
0148: * @param URL The URL of the image file.
0149: * @returns image The number of the image. Use this index to pass the image to other methods in the tree buffer.
0150: */
0151:
0152: public int addImage(String URL) {
0153: for (int i = 0; i < _images.size(); i++)
0154: if (((String) _images.elementAt(i)).equals(URL))
0155: return i;
0156:
0157: _images.addElement(URL);
0158: return _images.size() - 1;
0159: }
0160:
0161: /**
0162: * This method returns true if the current node has any children.
0163: */
0164:
0165: public boolean areChildrenLoaded() {
0166: if (_current == null)
0167: return false;
0168:
0169: if (_current.getFirstChild() == null)
0170: return false;
0171:
0172: return true;
0173: }
0174:
0175: /**
0176: * This method removes any images that appear after the text.
0177: */
0178:
0179: public void clearAdditionalImages() {
0180: if (_current == null)
0181: return;
0182:
0183: _current.clearAdditionalImages();
0184: }
0185:
0186: /**
0187: * This method will clear all selected items in the tree.
0188: */
0189: public void clearSelections() {
0190: for (int i = 0; i < _list.size(); i++) {
0191: Object o = _list.elementAt(i);
0192: if (o != null && o instanceof TreeNode)
0193: ((TreeNode) o).setSelected(false);
0194: }
0195: }
0196:
0197: /**
0198: * This method will clear all selected items in the tree.
0199: */
0200: public void clearVisibleSelections() {
0201: traverse(new VisibleItemDeselector(), null);
0202: }
0203:
0204: /**
0205: * This method removes all the children of the current node from the tree.
0206: * @param deleteParent true to remove the current node as well as its children.
0207: */
0208:
0209: public void deleteChildren(boolean deleteParent) {
0210: if (_current == null)
0211: return;
0212:
0213: TreeNode this One = _current;
0214:
0215: TreeNode child = _current.getFirstChild();
0216: while (child != null) {
0217: TreeNode next = child.getNextNode();
0218: _current = child;
0219: deleteChildren(true);
0220: child = next;
0221: }
0222:
0223: if (!deleteParent)
0224: _current = this One;
0225: else {
0226: int handle = this One.getHandle();
0227: TreeNode parent = this One.getParent();
0228: TreeNode next = this One.getNextNode();
0229: TreeNode prior = this One.getPriorNode();
0230:
0231: if (parent != null) {
0232: if (parent.getFirstChild() == this One)
0233: parent.setFirstChild(next);
0234: if (parent.getLastChild() == this One)
0235: parent.setLastChild(prior);
0236: }
0237:
0238: if (next != null)
0239: next.setPriorNode(prior);
0240: if (prior != null)
0241: prior.setNextNode(next);
0242:
0243: this One.reset();
0244: if (_root == this One)
0245: _root = null;
0246: if (_current == this One)
0247: _current = null;
0248: _current = _root;
0249:
0250: _list.setElementAt(new Integer(_nextListItem.intValue()),
0251: handle);
0252: _nextListItem = new Integer(handle);
0253: }
0254: }
0255:
0256: /**
0257: * This method writes the contents of the tree to System.error. It is useful for debugging.
0258: */
0259:
0260: public void dump() {
0261: System.err
0262: .println("=============================================================");
0263: System.err.println("Elements=" + _list.size());
0264: System.err.println("Next element=" + _nextListItem);
0265:
0266: for (int i = 0; i < _list.size(); i++) {
0267: Object o = _list.elementAt(i);
0268: if (o == null)
0269: System.err.println("Item" + i + " = null");
0270: else if (o instanceof Integer)
0271: System.err.println("Item " + i + " value = "
0272: + ((Integer) o).intValue());
0273: else {
0274: int parent, child, next;
0275: parent = -1;
0276: child = -1;
0277: next = -1;
0278:
0279: TreeNode t = (TreeNode) o;
0280:
0281: TreeNode p = t.getParent();
0282: if (p != null)
0283: parent = p.getHandle();
0284:
0285: TreeNode c = t.getFirstChild();
0286: if (c != null)
0287: child = c.getHandle();
0288:
0289: TreeNode n = t.getNextNode();
0290: if (n != null)
0291: next = n.getHandle();
0292:
0293: String text = t.getText();
0294:
0295: System.err.println("Item=" + i + " Text = " + text
0296: + " Parent = " + parent + " First Child = "
0297: + child + " next=" + next);
0298: }
0299: }
0300: }
0301:
0302: /**
0303: * This method returns the URL of the additional images in the node.
0304: */
0305: public String getAdditionalImage(int index) {
0306: if (_current == null)
0307: return new String("");
0308:
0309: int imgNo = _current.getAdditionalImage(index);
0310: return (String) _images.elementAt(imgNo);
0311: }
0312:
0313: /**
0314: * This method returns the number of additional images attached to this tree node.
0315: */
0316: public int getAdditionalImageCount() {
0317: if (_current == null)
0318: return -1;
0319:
0320: return _current.getAdditionalImageCount();
0321: }
0322:
0323: /**
0324: * This method returns the text of the additional images in the node.
0325: */
0326: public String getAdditionalText(int index) {
0327: if (_current == null)
0328: return new String("");
0329:
0330: return _current.getAdditionalText(index);
0331: }
0332:
0333: /**
0334: * This method returns the URL that the user will go to for the additional image in the node.
0335: */
0336: public String getAdditionalURL(int index) {
0337: if (_current == null)
0338: return new String("");
0339:
0340: return _current.getAdditionalURL(index);
0341: }
0342:
0343: /**
0344: * This method returns the value of an internal counter in the class. The counter will be incremented each time the method is called
0345: */
0346: public int getCount() {
0347: _count++;
0348: return _count;
0349: }
0350:
0351: /**
0352: * This method returns the a list of all the nodes in the tree.
0353: */
0354: public Enumeration getElements() {
0355: return new TreeEnumerator(_list);
0356: }
0357:
0358: /**
0359: * This method returns the handle of the current tree node.
0360: */
0361: public int getHandle() {
0362: int retval = -1;
0363: if (_current != null)
0364: retval = _current.getHandle();
0365: return retval;
0366: }
0367:
0368: /**
0369: * This method returns the URL of the image associated with the current tree node.
0370: */
0371: public String getImage() {
0372: String retval = null;
0373:
0374: if (_current != null) {
0375: int imageNo = _current.getImage();
0376: if ((imageNo > -1) && (imageNo < _images.size()))
0377: retval = (String) _images.elementAt(imageNo);
0378: }
0379:
0380: return retval;
0381: }
0382:
0383: /**
0384: * This method returns the URL that the user will go to if they click on in the current node.
0385: */
0386: public String getImageURL() {
0387: String retval = null;
0388: if (_current != null)
0389: retval = _current.getImageURL();
0390: return retval;
0391: }
0392:
0393: /**
0394: * This method returns the user data Object associated with the current node.
0395: */
0396: public Object getKey() {
0397: if (_current != null)
0398: return _current.getKey();
0399: else
0400: return null;
0401: }
0402:
0403: /**
0404: * This method returns the level of indentation for the current node.
0405: */
0406: public int getLevel() {
0407: int retval = -1;
0408: if (_current != null)
0409: retval = _current.getLevel();
0410: return retval;
0411: }
0412:
0413: /**
0414: * This method gets the expand / contract mode for the tree. Valid Values are MODE_SUBMIT (use submit images to expand, contract tree items) or MODE_LINK (use html links and javascript to expand / contract the tree).
0415: */
0416: public int getMode() {
0417: return _mode;
0418: }
0419:
0420: TreeNode getNode() {
0421: return _current;
0422: }
0423:
0424: /**
0425: * This method will return the tree node that will be scrolled to the next time the page is loaded.
0426: */
0427: public int getScrollTo() {
0428: int retval = _scrollTo;
0429: if (!itemExists(_scrollTo))
0430: retval = -1;
0431: _scrollTo = -1;
0432: return retval;
0433: }
0434:
0435: /**
0436: * This method returns whether the current node will be selectable.
0437: */
0438: public boolean getSelectable() {
0439: if (_current == null)
0440: return false;
0441: else
0442: return _current.getSelectable();
0443: }
0444:
0445: /**
0446: * This method returns the handle of the currently selected node.
0447: */
0448: public int getSelected() {
0449: int retval = _selected;
0450: return retval;
0451: }
0452:
0453: /**
0454: * If select mode is MODE_MANY, this will return whether the item is selected or not.
0455: */
0456: public boolean getSelected(int handle) {
0457: if (handle < 0)
0458: return false;
0459: if (handle >= _list.size())
0460: return false;
0461: Object o = _list.elementAt(handle);
0462: if (o == null)
0463: return false;
0464: if (o instanceof Integer)
0465: return false;
0466: return ((TreeNode) o).getSelected();
0467: }
0468:
0469: /**
0470: * This method returns the select mode for the tree. Valid values are SELECT_NONE or SELECT_ONE.
0471: */
0472: public int getSelectMode() {
0473: return _selectMode;
0474: }
0475:
0476: /**
0477: * This method returns whether or not the root of the tree is visible.
0478: */
0479:
0480: public boolean getShowRoot() {
0481: return _showRoot;
0482: }
0483:
0484: /**
0485: * This method returns the text associated with the current tree node.
0486: */
0487: public String getText() {
0488: String retval = null;
0489: if (_current != null)
0490: retval = _current.getText();
0491: return retval;
0492: }
0493:
0494: /**
0495: * This method returns the URL for the current node in the tree.
0496: */
0497: public String getURL() {
0498: String retval = null;
0499: if (_current != null)
0500: retval = _current.getURL();
0501: return retval;
0502: }
0503:
0504: /**
0505: * This method positions the current node to the first child of the current one. It will return false if the current node doesn't have a child.
0506: */
0507: public boolean gotoChild() {
0508: if (_current == null)
0509: return false;
0510: TreeNode n = _current.getFirstChild();
0511: if (n == null)
0512: return false;
0513: _current = n;
0514: return true;
0515: }
0516:
0517: /**
0518: * This method positions the current node to a particular item (handle) on the tree. It will return false an node with the specified handle doesn't exist.if the current node doesn't have a child.
0519: */
0520:
0521: public boolean gotoItem(int item) {
0522: if (item < 0)
0523: return false;
0524: if (item >= _list.size())
0525: return false;
0526: Object o = _list.elementAt(item);
0527: if (o == null)
0528: return false;
0529: if (o instanceof Integer)
0530: return false;
0531: _current = (TreeNode) o;
0532: return true;
0533: }
0534:
0535: /**
0536: * This method positions the current node to the last child of the current one. It will return false if the current node doesn't have a child.
0537: */
0538: public boolean gotoLastChild() {
0539: if (_current == null)
0540: return false;
0541: TreeNode n = _current.getLastChild();
0542: if (n == null)
0543: return false;
0544: _current = n;
0545: return true;
0546: }
0547:
0548: /**
0549: * This method positions the current node to the next node on the same level. It will return false if the current node is the last one on the branch.doesn't have a child.
0550: */
0551: public boolean gotoNext() {
0552: if (_current == null)
0553: return false;
0554: TreeNode n = _current.getNextNode();
0555: if (n == null)
0556: return false;
0557: if (n == _current)
0558: return false;
0559:
0560: _current = n;
0561: return true;
0562: }
0563:
0564: /**
0565: * This method positions the current node to the parent of the current. It will return false if the current node is the root.
0566: */
0567: public boolean gotoParent() {
0568: if (_current == null)
0569: return false;
0570: if (_current == _root)
0571: return false;
0572: TreeNode n = _current.getParent();
0573: _current = n;
0574: return true;
0575: }
0576:
0577: /**
0578: * This method positions the current node to the root node. It will return false if the tree is empty.
0579: */
0580: public boolean gotoRoot() {
0581: if (_root == null)
0582: return false;
0583: _current = _root;
0584: return true;
0585: }
0586:
0587: /**
0588: * This method returns true if the current node has children.
0589: */
0590: public boolean hasChildren() {
0591: if (_current == null)
0592: return false;
0593:
0594: if (_current.areThereChildren())
0595: return true;
0596:
0597: if (_current.getFirstChild() == null)
0598: return false;
0599:
0600: return true;
0601:
0602: }
0603:
0604: /**
0605: * This method returns true if the current node is the root node.
0606: */
0607: public boolean isCurrentRoot() {
0608: return (_current == _root);
0609: }
0610:
0611: /**
0612: * This method returns true if the tree is empty.
0613: */
0614: public boolean isEmpty() {
0615: return (_root == null);
0616: }
0617:
0618: /**
0619: * This method returns true if the current item on the tree is expanded.
0620: */
0621: public boolean isExpanded() {
0622: boolean retval = false;
0623: if (_current != null)
0624: retval = _current.isExpanded();
0625: return retval;
0626: }
0627:
0628: /**
0629: * This method returns true if the current item on the tree is visible.
0630: */
0631:
0632: public boolean isItemVisible() {
0633: return (getImage() != null);
0634: }
0635:
0636: /**
0637: * This method returns true if a node with the specified handle exists in the tree.
0638: */
0639:
0640: public boolean itemExists(int item) {
0641: if (item < 0)
0642: return false;
0643: if (item >= _list.size())
0644: return false;
0645: Object o = _list.elementAt(item);
0646: if (o == null)
0647: return false;
0648: if (o instanceof Integer)
0649: return false;
0650: return true;
0651: }
0652:
0653: /**
0654: * This method replace the value of the string "$UNO$" in the passed string with a number unique for the life of this tree buffer. It can be used to add uniqueness to URLs to defeat browser caching for pages that should not be cached.
0655: * @returns the new String.
0656: */
0657:
0658: public String replaceUno(String st) {
0659: int pos = st.indexOf("$UNO$");
0660: if (pos < 0)
0661: return st;
0662:
0663: return (st.substring(0, pos) + getCount() + st
0664: .substring(pos + 5));
0665: }
0666:
0667: /**
0668: * Seeds an internal counter in the tree.
0669: */
0670:
0671: public void setCount(int count) {
0672: _count = count;
0673: }
0674:
0675: /**
0676: * This method expands or contracts the current tree node.
0677: */
0678:
0679: public void setExpanded(boolean expanded) {
0680: if (_current != null)
0681: _current.setExpanded(expanded);
0682: }
0683:
0684: /**
0685: * This method tells the tree whether the tree has children nodes. It will place an expand icon on the tree node even if the children aren't loaded yet.
0686: */
0687: public void setHasChildren(boolean hasChildren) {
0688: if (_current != null) {
0689: _current.setHasChildren(hasChildren);
0690: if (!hasChildren)
0691: _current.setExpanded(false);
0692: }
0693: }
0694:
0695: /**
0696: * This sets key information associated with the current tree node.
0697: */
0698: public void setKey(Object key) {
0699: if (_current != null)
0700: _current.setKey(key);
0701: }
0702:
0703: /**
0704: * This method sets the expand / contract mode for the tree. Valid Values are MODE_SUBMIT (use submit images to expand, contract tree items) or MODE_LINK (use html links and javascript to expand / contract the tree).
0705: */
0706: public void setMode(int mode) {
0707: _mode = mode;
0708: }
0709:
0710: /**
0711: * This method returns the tree item that will be scrolled to the next time the tree is displayed in the browser.
0712: */
0713: public void setScrollTo(int handle) {
0714: _scrollTo = handle;
0715: }
0716:
0717: /**
0718: * This method returns whether the current node will be selectable.
0719: */
0720: public void setSelectable(boolean sel) {
0721: if (_current != null)
0722: _current.setSelectable(sel);
0723: }
0724:
0725: /**
0726: * This method sets the currently selected node.
0727: */
0728: public void setSelected(int handle) {
0729: _selected = handle;
0730: }
0731:
0732: /**
0733: * If select mode is MODE_MANY, this will set the item to either selected or not.
0734: */
0735: public void setSelected(int handle, boolean trueFalse) {
0736: if (handle < 0)
0737: return;
0738: if (handle >= _list.size())
0739: return;
0740: Object o = _list.elementAt(handle);
0741: if (o == null)
0742: return;
0743: if (o instanceof Integer)
0744: return;
0745: ((TreeNode) o).setSelected(trueFalse);
0746: }
0747:
0748: /**
0749: * This method sets the select mode for the tree. Valid Values are SELET_ONE, SELECT_NONE and SELECT_MANY.
0750: */
0751: public void setSelectMode(int selectMode) {
0752: _selectMode = selectMode;
0753: }
0754:
0755: /**
0756: * This method shows or hides the root tree node.
0757: */
0758: public void setShowRoot(boolean show) {
0759: _showRoot = show;
0760: }
0761:
0762: /**
0763: * This method sets the text associated with the current tree node.
0764: */
0765: public void setText(String text) {
0766: if (_current != null)
0767: _current.setText(text);
0768: }
0769:
0770: /**
0771: * This method sets the URL associated with the current tree node.
0772: */
0773:
0774: public void setURL(String url) {
0775: if (_current != null)
0776: _current.setURL(url);
0777: }
0778:
0779: /**
0780: * For Trees with node components, you must specify a row in the datastore that corresponds to the items for the current node.
0781: * @param rowNo
0782: */
0783: public void setRow(int rowNo) {
0784: if (_current != null)
0785: _current.setDsRowNo(rowNo);
0786: }
0787:
0788: /**
0789: * For Trees with node components, the datastore row associated with each component
0790: * @param rowNo
0791: */
0792: public int getRow() {
0793: if (_current != null)
0794: return _current.getDsRowNo();
0795: else
0796: return -1;
0797: }
0798:
0799: /**
0800: * Use this method to traverse the tree. When each node is visited in the traversal the callback method in the TreeTraversalCallBack object will be invoked and passed the PrintWriter.
0801: * @see TreeTraversalCallBack#callback
0802: */
0803:
0804: public void traverse(TreeTraversalCallBack cb, PrintWriter p) {
0805: traverse(cb, p, true);
0806: }
0807:
0808: /**
0809: * Use this method to traverse the tree. When each node is visited in the traversal the callback method in the TreeTraversalCallBack object will be invoked and passed the PrintWriter.
0810: * @see TreeTraversalCallBack#callback
0811: */
0812:
0813: public void traverse(TreeTraversalCallBack cb, PrintWriter p,
0814: boolean onlyExpanded) {
0815: _ttcb = cb;
0816: gotoRoot();
0817: int cur = getHandle();
0818:
0819: if (_showRoot) {
0820: _ttcb.callBack(this , p);
0821: gotoRoot();
0822: }
0823:
0824: if (onlyExpanded) {
0825: if (isExpanded())
0826: traverseChildren(cur, p);
0827: } else {
0828: traverseAllChildren(cur, p);
0829: }
0830:
0831: _ttcb = null;
0832:
0833: }
0834:
0835: private void traverseChildren(int item, PrintWriter p) {
0836: if (!gotoItem(item))
0837: return;
0838:
0839: if (!gotoChild())
0840: return;
0841:
0842: do {
0843: int cur = getHandle();
0844: _ttcb.callBack(this , p);
0845: gotoItem(cur);
0846: if (isExpanded())
0847: traverseChildren(cur, p);
0848: gotoItem(cur);
0849: } while (gotoNext());
0850: }
0851:
0852: //Added the private method to traverse All Children regardless
0853: //if the children are expanded or collapsed.
0854: //Called by traverse.
0855: //LS - 02/20/04
0856: private void traverseAllChildren(int item, PrintWriter p) {
0857: if (!gotoItem(item))
0858: return;
0859:
0860: if (!gotoChild())
0861: return;
0862:
0863: do {
0864: int cur = getHandle();
0865: _ttcb.callBack(this , p);
0866: gotoItem(cur);
0867: // if (isExpanded())
0868: traverseAllChildren(cur, p);
0869: gotoItem(cur);
0870: } while (gotoNext());
0871: }
0872:
0873: /**
0874: * This method positions the current node to the prior node on the same level. It will return false if the current node is the parent of the one on the branch.
0875: */
0876: public boolean gotoPrior() {
0877: if (_current == null)
0878: return false;
0879: TreeNode n = _current.getPriorNode();
0880: if (n == null)
0881: return false;
0882: if (n == _current)
0883: return false;
0884:
0885: _current = n;
0886: return true;
0887: }
0888:
0889: /**
0890: * Changes the images used by the node
0891: */
0892: public void setImages(int image, int expandedImage) {
0893: if (_current == null)
0894: return;
0895: _current.setImages(image, expandedImage);
0896: }
0897:
0898: /**
0899: * Add header components to the tree. The components will appear above the tree columns
0900: */
0901: public void addHeaderComponent(HtmlComponent comp) {
0902: if (_headers == null)
0903: _headers = new Vector();
0904: Object o[] = new Object[3];
0905: o[0] = comp;
0906: _headers.add(o);
0907: }
0908:
0909: /**
0910: * Add header components to the tree. The components will appear above the tree columns
0911: */
0912: public void addHeaderComponent(HtmlComponent comp, String align,
0913: String width) {
0914: if (_headers == null)
0915: _headers = new Vector();
0916: Object o[] = new Object[3];
0917: o[0] = comp;
0918: o[1] = align;
0919: o[2] = width;
0920: _headers.add(o);
0921: }
0922:
0923: /**
0924: * remove all header components from the tree
0925: */
0926: public void resetHeaders() {
0927: _headers.removeAllElements();
0928: }
0929:
0930: /**
0931: * @return the header component at a specific position
0932: */
0933: public HtmlComponent getHeaderComponent(int index) {
0934: Object o[] = (Object[]) _headers.elementAt(index);
0935: return (HtmlComponent) o[0];
0936: }
0937:
0938: /**
0939: * @return the header alignment at a specific position
0940: */
0941: public String getHeaderAlign(int index) {
0942: Object o[] = (Object[]) _headers.elementAt(index);
0943: return (String) o[1];
0944: }
0945:
0946: /**
0947: * @return the header width at a specific position
0948: */
0949: public String getHeaderWidth(int index) {
0950: Object o[] = (Object[]) _headers.elementAt(index);
0951: return (String) o[2];
0952: }
0953:
0954: public int getHeaderCount() {
0955: if (_headers == null)
0956: return 0;
0957: else
0958: return _headers.size();
0959: }
0960:
0961: /**
0962: * Add node components to the tree. The components will in new TDs after each node. Note, if you add nodes to trees with headers, the first header goes over the tree node, so the number of node components should number of headers - 1.
0963: */
0964: public void addNodeComponent(HtmlComponent comp) {
0965: if (_nodeComponents == null)
0966: _nodeComponents = new Vector();
0967: _nodeComponents.add(comp);
0968: }
0969:
0970: /**
0971: * remove all node components from the tree
0972: */
0973: public void resetNodeComponents() {
0974: _nodeComponents.removeAllElements();
0975: }
0976:
0977: /**
0978: * @return a vector of HTML components that will appear after each node.
0979: */
0980: public Vector getNodeComponents() {
0981: return _nodeComponents;
0982: }
0983:
0984: public boolean canSortOnColumn(int colNo) {
0985: if (colNo == 0)
0986: return true;
0987: HtmlComponent comp = findSortComponent(colNo);
0988: if (comp == null)
0989: return false;
0990:
0991: return (findSortComponent(comp) != null);
0992: }
0993:
0994: private HtmlComponent findSortComponent(int col) {
0995: if (col == 0)
0996: return null;
0997: else {
0998: Vector nodeComps = getNodeComponents();
0999: if (nodeComps == null)
1000: return null;
1001: else if (col > nodeComps.size())
1002: return null;
1003: else
1004: return (HtmlComponent) nodeComps.elementAt(col - 1);
1005: }
1006: }
1007:
1008: private HtmlComponent findSortComponent(HtmlComponent sortComp) {
1009: if (sortComp instanceof HtmlFormComponent) {
1010: if (((HtmlFormComponent) sortComp).getColumnNumber() != -1)
1011: return sortComp;
1012: } else if (sortComp instanceof HtmlText) {
1013: if (((HtmlText) sortComp).getExpressionEvaluator() != null)
1014: return sortComp;
1015: } else if (sortComp instanceof HtmlContainer) {
1016: Enumeration e = ((HtmlContainer) sortComp).getComponents();
1017: while (e.hasMoreElements()) {
1018: sortComp = (HtmlComponent) e.nextElement();
1019: return findSortComponent(sortComp);
1020: }
1021: }
1022: return null;
1023: }
1024:
1025: /**
1026: * Sort the tree
1027: * @param column the number of the header column to sort on (0 for path name only sort)
1028: * @param pathDir the sort direction for the tree path (first column) to sort on SORT_ASC, SORT_DES or SORT_ANY to ignore the path sort
1029: * @param dir the direction to sort the specified column on SORT_ASC or SORT_DES if column is not zero
1030: */
1031: public synchronized void sort(int column, int pathDir, int dir) {
1032: DataStoreBuffer ds = null;
1033: DataStoreEvaluator dsEval = null;
1034: int dsSortColumn = -1;
1035: HtmlComponent comp = findSortComponent(column);
1036: if (comp != null) {
1037: comp = findSortComponent(comp);
1038: if (comp instanceof HtmlFormComponent) {
1039: dsSortColumn = ((HtmlFormComponent) comp)
1040: .getColumnNumber();
1041: ds = ((HtmlFormComponent) comp).getBoundDataStore();
1042: } else if (comp instanceof HtmlText) {
1043: dsEval = ((HtmlText) comp).getExpressionEvaluator();
1044: ds = dsEval.getDataStore();
1045: String exp = dsEval.getExpression();
1046: int index = -1;
1047: if (exp != null)
1048: dsSortColumn = ds.getColumnIndex(exp);
1049: }
1050: } else
1051: dir = pathDir;
1052:
1053: TreeNode parent = _root;
1054: VectorSort sorter = null;
1055: if (pathDir == SORT_ANY)
1056: sorter = new SorterNoPath(dir, dsSortColumn, dsEval, ds);
1057: else
1058: sorter = new Sorter(pathDir, dir);
1059:
1060: for (int i = 0; i < _list.size(); i++) {
1061: TreeNode n = (TreeNode) _list.elementAt(i);
1062: if (n != null && n != _root) {
1063: SortElement ele = new SortElement();
1064: ele.node = n;
1065: ele.parent = n.getParent();
1066: ele.path = n.getFullPath();
1067: try {
1068: if (dsSortColumn != -1)
1069: ele.value = ds.getAny(ele.node.getDsRowNo(),
1070: dsSortColumn);
1071: else if (dsEval != null)
1072: ele.value = dsEval.evaluateRow(ele.node
1073: .getDsRowNo());
1074: else {
1075: if (ele.node.getFirstChild() == null)
1076: ele.value = n.getText();
1077: }
1078: } catch (DataStoreException e) {
1079: MessageLog.writeErrorMessage("sort()", e, this );
1080: }
1081: sorter.add(ele);
1082: }
1083: }
1084: TreeNode selected = null;
1085: if (_selected != -1) {
1086: if (gotoItem(_selected))
1087: selected = _current;
1088: }
1089:
1090: sorter.sort();
1091: gotoRoot();
1092: deleteChildren(false);
1093: for (int i = 0; i < sorter.size(); i++) {
1094: SortElement ele = (SortElement) sorter.elementAt(i);
1095: _current = ele.parent;
1096: int handle = addChild(ele.node);
1097: if (ele.node == selected)
1098: _selected = handle;
1099: }
1100: }
1101:
1102: private class SortElement {
1103: TreeNode node;
1104: TreeNode parent;
1105: String path;
1106: Object value;
1107: }
1108:
1109: private class Sorter extends VectorSort {
1110: private int _pathDir, _elementDir;
1111:
1112: public Sorter(int pathDir, int eleDir) {
1113: _pathDir = pathDir;
1114: _elementDir = eleDir;
1115: }
1116:
1117: public boolean compare(Object o1, Object o2) {
1118: SortElement s1 = (SortElement) o1;
1119: SortElement s2 = (SortElement) o2;
1120: boolean ret = compareElements(s1, s2);
1121: return ret;
1122: }
1123:
1124: public boolean compareElements(SortElement ele1,
1125: SortElement ele2) {
1126: String path1 = ele1.path;
1127: String path2 = ele2.path;
1128: Object val1 = ele1.value;
1129: Object val2 = ele2.value;
1130: int level1 = ele1.node.getLevel();
1131: int level2 = ele2.node.getLevel();
1132:
1133: if (level1 != level2)
1134: return level2 > level1;
1135:
1136: int ret = path1.compareToIgnoreCase(path2);
1137: if (ret != 0) {
1138: if (_pathDir == SORT_ASC)
1139: return ret < 0;
1140: else
1141: return ret > 0;
1142: }
1143:
1144: if (val1 == null) {
1145: if (val2 == null)
1146: ret = 0;
1147: else
1148: ret = -1;
1149: } else if (val2 == null)
1150: ret = 1;
1151: else if (val1 instanceof String) {
1152: ret = ((String) val1)
1153: .compareToIgnoreCase((String) val2);
1154: } else if (val1 instanceof Number) {
1155: double v1 = ((Number) val1).doubleValue();
1156: double v2 = ((Number) val2).doubleValue();
1157: if (v1 == v2)
1158: ret = 0;
1159: else if (v1 < v2)
1160: ret = -1;
1161: else
1162: ret = 1;
1163: } else if (val1 instanceof Timestamp) {
1164: if (((Timestamp) val1).equals((Timestamp) val2))
1165: ret = 0;
1166: else if (((Timestamp) val1).before((Timestamp) val2))
1167: ret = -1;
1168: else
1169: ret = 1;
1170: } else if (val1 instanceof java.sql.Date) {
1171: if (val1.equals(val2))
1172: ret = 0;
1173: else if (((java.sql.Date) val1)
1174: .before((java.sql.Date) val2))
1175: ret = -1;
1176: else
1177: ret = 1;
1178: } else if (val1 instanceof java.sql.Time) {
1179: if (val1.equals(val2))
1180: ret = 0;
1181: else if (((java.sql.Time) val1)
1182: .before((java.sql.Time) val2))
1183: ret = -1;
1184: else
1185: ret = 1;
1186: }
1187:
1188: if (_elementDir == SORT_ASC)
1189: return ret < 0;
1190: else
1191: return ret > 0;
1192: }
1193: }
1194:
1195: private class SorterNoPath extends VectorSort {
1196: private int _elementDir;
1197: private int _sortCol;
1198: private DataStoreEvaluator _eval;
1199: private DataStoreBuffer _ds;
1200: private Stack _left = new Stack();
1201: private Stack _right = new Stack();
1202:
1203: public SorterNoPath(int eleDir, int sortColumn,
1204: DataStoreEvaluator dsEval, DataStoreBuffer ds) {
1205: _elementDir = eleDir;
1206: _sortCol = sortColumn;
1207: _eval = dsEval;
1208: _ds = ds;
1209: }
1210:
1211: public boolean compare(Object o1, Object o2) {
1212: SortElement s1 = (SortElement) o1;
1213: SortElement s2 = (SortElement) o2;
1214: boolean ret = compareElements(s1, s2);
1215: return ret;
1216: }
1217:
1218: private Object RetrieveValue(TreeNode node) {
1219: Object retVal = null;
1220: if (node != null) {
1221: try {
1222: retVal = node.getText();
1223: if (_sortCol != -1)
1224: retVal = _ds
1225: .getAny(node.getDsRowNo(), _sortCol);
1226: else if (_eval != null)
1227: retVal = _eval.evaluateRow(node.getDsRowNo());
1228: } catch (DataStoreException e) {
1229: MessageLog.writeErrorMessage("sort()", e, this );
1230: }
1231: }
1232: return retVal;
1233: }
1234:
1235: /*
1236: * given two tree nodes, start at their most extreme parent, and start
1237: * comparing each node in their corresponding paths.
1238: *
1239: * Return the first pair of nodes in their paths that is NOT equal in id.
1240: */
1241: private Pair RetrieveFirstNotEqualinPath(TreeNode left,
1242: TreeNode right) {
1243: Pair retVal = new Pair();
1244: _left.clear();
1245: _right.clear();
1246:
1247: while (left != null) {
1248: _left.push(left);
1249: left = left.getParent();
1250: }
1251:
1252: while (right != null) {
1253: _right.push(right);
1254: right = right.getParent();
1255: }
1256:
1257: boolean empty = _left.empty() || _right.empty();
1258: boolean equals = true;
1259: TreeNode leftCandidate = null;
1260: TreeNode rightCandidate = null;
1261: while (!empty && equals) {
1262: leftCandidate = (TreeNode) _left.pop();
1263: rightCandidate = (TreeNode) _right.pop();
1264: equals = leftCandidate.equals(rightCandidate);
1265: empty = _left.empty() || _right.empty();
1266: }
1267:
1268: if (equals) {
1269: if (_left.empty())
1270: leftCandidate = null;
1271:
1272: if (_right.empty())
1273: rightCandidate = null;
1274: }
1275:
1276: retVal.left = leftCandidate;
1277: retVal.right = rightCandidate;
1278: return retVal;
1279: }
1280:
1281: public boolean compareElements(SortElement ele1,
1282: SortElement ele2) {
1283: Object val1 = ele1.value;
1284: Object val2 = ele2.value;
1285: int ret = 0;
1286:
1287: // Retrieve first two nodes in paths that are not equal
1288: Pair nodePair = RetrieveFirstNotEqualinPath(ele1.node,
1289: ele2.node);
1290:
1291: // Retrieve the corresponding column value that we are sorting on.
1292: val1 = RetrieveValue((TreeNode) nodePair.left);
1293: val2 = RetrieveValue((TreeNode) nodePair.right);
1294:
1295: // Compare the values.
1296: if (val1 == null) {
1297: if (val2 == null)
1298: ret = 0;
1299: else
1300: ret = -1;
1301: } else if (val2 == null)
1302: ret = 1;
1303: else if (val1 instanceof String) {
1304: ret = ((String) val1)
1305: .compareToIgnoreCase((String) val2);
1306: } else if (val1 instanceof Number) {
1307: double v1 = ((Number) val1).doubleValue();
1308: double v2 = ((Number) val2).doubleValue();
1309: if (v1 == v2)
1310: ret = 0;
1311: else if (v1 < v2)
1312: ret = -1;
1313: else
1314: ret = 1;
1315: } else if (val1 instanceof Timestamp) {
1316: if (((Timestamp) val1).equals((Timestamp) val2))
1317: ret = 0;
1318: else if (((Timestamp) val1).before((Timestamp) val2))
1319: ret = -1;
1320: else
1321: ret = 1;
1322:
1323: } else if (val1 instanceof java.sql.Date) {
1324: if (val1.equals(val2))
1325: ret = 0;
1326: else if (((java.sql.Date) val1)
1327: .before((java.sql.Date) val2))
1328: ret = -1;
1329: else
1330: ret = 1;
1331: } else if (val1 instanceof java.sql.Time) {
1332: if (val1.equals(val2))
1333: ret = 0;
1334: else if (((java.sql.Time) val1)
1335: .before((java.sql.Time) val2))
1336: ret = -1;
1337: else
1338: ret = 1;
1339: }
1340:
1341: boolean retVal = false;
1342: if (_elementDir == SORT_ASC)
1343: retVal = ret < 0;
1344: else
1345: retVal = ret > 0;
1346:
1347: return retVal;
1348: }
1349: }
1350:
1351: private class Pair {
1352: public Object left;
1353: public Object right;
1354: }
1355: }
|