All Downloads are FREE. Search and download functionalities are using the official Maven repository.

at.spardat.xma.mdl.tree.TreeNode Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2003, 2007 s IT Solutions AT Spardat GmbH .
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     s IT Solutions AT Spardat GmbH - initial API and implementation
 *******************************************************************************/

// @(#) $Id: TreeNode.java 2089 2007-11-28 13:56:13Z s3460 $
package at.spardat.xma.mdl.tree;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;

import org.eclipse.swt.widgets.TreeItem;

import at.spardat.enterprise.util.StringUtil;
import at.spardat.xma.serializer.XmaInput;
import at.spardat.xma.serializer.XmaOutput;

/**
 * Represents a node in a tree widget model.
 * 
 * @author YSD, 03.05.2003 20:11:50
 */
public class TreeNode {
    
    private static final String DUMMY_NODE = "at.spardat.xma.dummynode";
    
    // an empty array of TreeNodes
    private static final TreeNode[]     emptyNodeArray = new TreeNode[0];
    
    // the displayed text string of this node. Never null.
    String                              text_;
    
    // the key of this node; never null
    String                              key_;
    
    // the id of an displayed image; is zero if no image is set
    short                               imageId_;
    
    /**
     * properties encoded in a string; may be null, if no property is set;
     * properties are encoded in the form p1=val1,p2=val2,p2=val3,...
     */
    String                              props_;
    
    // list of TreeNodes which are the sons of this node; may be null;
    ArrayList                           childs_;
    
    // either a TreeNode or the tree itself (if the node is a root); never null;
    Object                              parent_;
    
    // the tree to which this node belongs
    TreeWM                              tree_;
    
    // indicates that this has been removed and cannot be accessed anymore.
    boolean                             dead_;
    
    //indicates that this node has a leazy behauvior node.
    //the dummynode is removed before the expand event, 
    //the lazy_ is set to true before and to false after the expand event
    private boolean                             lazy_;
    
    // reserved property key for foreground color
    static final String                 PROP_KEY_FOREGROUND_COLOR = "f";
    
    // reserved property key for background color
    static final String                 PROP_KEY_BACKGROUND_COLOR = "b";    
      
    /**
     * Constructs a TreeNode and makes it the child of parent at
     * a specified zero based index in the child sequence.
     * 
     * @param parent may be ITreeWM in which case this node becomes a
     *                root node or a TreeNode in which case this becomes
     *                a direct child of parent.
     * @param index  the zero based index into the sequence of childs this node should
     *                get. 0 inserts in front, getChildCount() inserts at the tail
     *                of the childs.
     * @param key    the key of the new node. This key must not already be contained
     *                in the tree.
     * @param text   the text that is displayed in the node
     * @param imageId an optinal id of an image to be displayed. Specify 0 if
     *                 no image should be displayed.
     * 
     * @param addDummyNode adds a dummy node as child to this node to make this node expandable. 
     * 						The dummy node is automatically removed at the client side if an expand event occurs on this node.  
     * 
     * @exception RuntimeException on invalid arguments.
     */
    public TreeNode (Object parent, int index, String key, String text, int imageId, boolean addDummyNode) {
        TreeWM    pTree = null;
        TreeNode        pNode = null;
        if (parent instanceof TreeWM) pTree = (TreeWM)parent;
        else if (parent instanceof TreeNode) pNode = (TreeNode) parent;
        else throw new IllegalArgumentException();
        if (index == -1) {
            index = (pTree == null ? pNode.getChildCount() : pTree.getRootCount());
        }
        tree_ = (pNode == null ? pTree : pNode.tree_); 
        if (tree_.containsKey (key)) throw new IllegalArgumentException ("duplicate key");
        String parentKey = null;
        if (pNode != null) parentKey = pNode.key_;
        // execute AddNodeEvent
        if (!tree_.handle(tree_.new AddNodeEvent (this, key, parentKey, index, text, (short)imageId, false)))
            throw new IllegalArgumentException (); 
        
        if(addDummyNode){
            addDummyNode();
        }
    }
    
    /**
     * Constructs a TreeNode and makes it the child of parent at
     * a specified zero based index in the child sequence.
     * 
     * @param parent may be ITreeWM in which case this node becomes a
     *                root node or a TreeNode in which case this becomes
     *                a direct child of parent.
     * @param index  the zero based index into the sequence of childs this node should
     *                get. 0 inserts in front, getChildCount() inserts at the tail
     *                of the childs.
     * @param key    the key of the new node. This key must not already be contained
     *                in the tree.
     * @param text   the text that is displayed in the node
     * @param imageId an optinal id of an image to be displayed. Specify 0 if
     *                 no image should be displayed.
     * @exception RuntimeException on invalid arguments.
     */
    public TreeNode (Object parent, int index, String key, String text, int imageId) {        
        this(parent, index, key, text, imageId, false);
    }
    

    
    /**
     * Constructs a TreeNode and makes it the last child of parent.
     * 
     * @param parent may be TreeWM in which case this node becomes a
     *                root node or a TreeNode in which case this becomes
     *                a direct child of parent.
     * @param key    the key of the new node. This key must not already be contained
     *                in the tree.
     * @param text   the text that is displayed in the node
     * @param imageId an optinal id of an image to be displayed. Specify 0 if
     *                 no image should be displayed.
     * @param addDummyNode adds a dummy node as child to this node to make this node expandable. 
     * 						The dummy node is automatically removed at the client side if an expand event occurs on this node.  
     * 
     * @exception RuntimeException on invalid arguments.
     */
    public TreeNode (Object parent, String key, String text, int imageId,boolean addDummyNode) {
        this (parent, -1, key, text, imageId, addDummyNode);
    }    
    
    /**
     * Constructs a TreeNode and makes it the last child of parent.
     * 
     * @param parent may be TreeWM in which case this node becomes a
     *                root node or a TreeNode in which case this becomes
     *                a direct child of parent.
     * @param key    the key of the new node. This key must not already be contained
     *                in the tree.
     * @param text   the text that is displayed in the node
     * @param imageId an optinal id of an image to be displayed. Specify 0 if
     *                 no image should be displayed.     
     * @exception RuntimeException on invalid arguments.
     */
    public TreeNode (Object parent, String key, String text, int imageId) {
        this (parent, -1, key, text, imageId, false);
    } 
    
    /**
     * Internal use constructor.
     */
    protected TreeNode () {
    }
    
    /**
     * Tests if a dummy node exists to this node.
     * (A dummy node created by addDummyNode() or by the TreeNode constructor)
     * @return true if a dummy node exists to this node - otherwise false.
     * @since version_number
     * @author s3460
     */
    public boolean hasDummyNode(){        
        return tree_.getNode(this.getKey()+DUMMY_NODE)!=null?true:false;        
    }
    
    /**
     * Adds a dummy node as child to this node to make this node expandable. 
     * The dummy node is automatically removed at the client side if an expand event occurs on this node.  
     * There can only exitst one dummy node for every node. Therefore a repeated call to this method does nothing.
     * @return true - if a dummy node was added, false otherwise. 
     * @since version_number
     * @author s3460
     */
    public boolean addDummyNode(){              
        if(!hasDummyNode()){
            TreeNode dummyNode = new TreeNode(this,0,getKey()+DUMMY_NODE,"",0);            
            return true;
        }  
        return false;
    }
    
    /**
     * Removes a dummy node created by addDummyNode() or by the TreeNode constructor.
     * If no dummy node exists thsi method does nothing.      
     * This method is called by the XMA framework if an expand event occurs on this node.
     * 
     * @return true - if a dummy node was removed, false otherwise. 
     * @since version_number
     * @author s3460
     */
    public boolean removeDummyNode(){
        TreeNode dummy = tree_.getNode(this.getKey()+DUMMY_NODE);
        if(dummy != null){
            dummy.remove();
            return true;
        }
        return false;
    }
    
    /**
     * Tests if a node is a dummy node created by addDummyNode() or by the TreeNode constructor.
     * @return true if this node is a dummy node.
     * @since version_number
     * @author s3460
     */
    public boolean isDummyNode(){
        return this.getKey().endsWith(DUMMY_NODE);
    }  
    
    
    /**
     * Returns an array of TreeNode objects that are the childs of
     * this TreeNode. 
     * 
     * @return the childs of this. The array is empty if this does
     *          not has childs.
     */
    public TreeNode [] getChilds () {
        checkDead();
        if (childs_ == null) return emptyNodeArray;
        TreeNode []     childs = new TreeNode[childs_.size()];
        Iterator iter = childs_.iterator();
        for (int i=0; iter.hasNext(); i++) {
            childs[i] = (TreeNode) iter.next();
        }
        return childs;
    }
    
    /**
     * Returns the child at the zero based index i which must
     * be greater equal zero and less than getChildCount().
     * 
     * @param i zero based index of the child
     * @return the requested child
     * @exception RuntimeException if index is invalid
     */
    public TreeNode getChild (int i) {
        return (TreeNode) childs_.get(i);
    }
    
    /**
     * Returns the parent of this tree node or null, if this
     * node is a root node.
     */
    public TreeNode getParent () {
        checkDead();
        if (parent_ instanceof TreeNode) return (TreeNode) parent_;
        return null;
    }
    
    /**
     * Returns the number of childs this node has.
     */
    public int getChildCount () {
        checkDead();
        if (childs_ == null) return 0;
        return childs_.size();
    }
    
    /**
     * Returns the image id of the image of this node or 0 if none has been set.
     */
    public short getImageId() {
        checkDead();
        return imageId_;
    }

    /**
     * Returns the key of this node.
     *  
     * @return the key which is never null.
     */
    public String getKey() {
        checkDead();
        return key_;
    }

    /**
     * Returns the text of this node. 
     * 
     * @return the text wich is never null.
     */
    public String getText() {
        checkDead();
        return text_;
    }
    
    /**
     * Sets a new image id.
     */
    public void setImageId (short s) {
        checkDead();
        tree_.handle(tree_.new ChangeNodeEvent(key_, text_, s, props_, false));
    }

    /**
     * Sets a new text.
     * 
     * @exception IllegalArgumentException if text is null.
     */
    public void setText (String string) {
        if (string == null) throw new IllegalArgumentException();
        checkDead();
        tree_.handle(tree_.new ChangeNodeEvent(key_, string, imageId_, props_, false));
    }
    
    /**
     * Removes this tree node from the tree. This method also removes
     * all nodes which are direct or indirect childs of this node.
     * You cannot access this and its direct or indirect childs 
     * anymore after this method terminates.
     */
    public void remove () {
        checkDead();
        tree_.handle(tree_.new RemoveNodeEvent(key_, false));
    }
    
    /**
     * Marks this tree node to be selected. Calling this method is the
     * same as calling select(key) on the tree widget model.
     */
    public void select () {
        checkDead();
        tree_.select(key_);
    }

    /**
     * Deselects this tree node if it is selected. If it is not selected,
     * this method does nothing. The same as calling deselect(key)
     * on the tree widget model.
     */
    public void deselect () {
        checkDead();
        tree_.deselect(key_);
    }
    
    /**
     * Returns true if this tree node is selected, false otherwise.
     */
    public boolean isSelected () {
        checkDead();
        return tree_.isSelected(key_);        
    }
    
    /**
     * Sets the expansion status of this tree node. 

* * This method must not * be called at the server side, but only on the client side in the * state where the UI is created. If the UI is not created yet or * this method is called in server side code, this method does nothing. * * @param expanded true, if this tree node should be shown in an expanded * state. */ public void setExpanded (boolean expanded) { checkDead(); TreeUIDelegateClient uiDel = getUIDelegate(); if (uiDel != null) uiDel.setExpanded(this, expanded, false); } /** * At the client side, this TreeNode has a representation in a UI library. * In the case of SWT, every TreeNode has a SWT TreeItem assigned. * If you want to set some visual attributes on the TreeItem, you * may call this method and cast the returned Object down to a SWT-TreeItem.

* * You must not change the text or the image of the returned * SWT TreeItem. If you want to change these attributes, use * either setText or setImageId in this class. * * @return if SWT is your UI-library, returns a SWT-TreeItem if your * code is running at the client side and you have a constructed UI. * Otherwise returns null. Note that the SWT UI widget (Tree) * must be alive in order for this method to work. */ public Object getUITreeItem () { checkDead(); TreeUIDelegateClient uiDel = getUIDelegate(); if (uiDel != null) return uiDel.node2item(this); return null; } /** * This is the inverse method to getUITreeItem. It returns the TreeNode * that corresponds to the tree item of the underlying UI library. In the case of SWT, * the argument must be an instance of class TreeItem.

* * @param uiTreeItem SWT-TreeItem if SWT the UI-library you are using. Must not be null. * The SWT-TreeItem must not be disposed. * @return TreeNode for the given TreeItem */ public static TreeNode getTreeNodeFor (Object uiTreeItem) { if (uiTreeItem == null) throw new IllegalArgumentException (); return TreeUIDelegateClient.item2node((TreeItem)uiTreeItem); } /** * Returns the UIDelegate of the widget model or null, if we are executing at * the server or the UI is not constructed yet. */ private TreeUIDelegateClient getUIDelegate () { if (!tree_.getPage().isAtServer()) { TreeUIDelegateClient uiDel = (TreeUIDelegateClient) ((TreeWMClient)tree_).getUIDelegate(); if (uiDel.isUIAttached()) return uiDel; } return null; } /** * Returns true if this node is a root node, i.e., there is no other node * where one of its childs is this node. */ public boolean isRoot () { checkDead(); return parent_ == tree_; } /** * Returns true if this node does not have child nodes. */ public boolean isLeaf () { checkDead(); return childs_ == null || childs_.size() == 0; } /** * Sets the foreground color of this TreeNode. * * @param c object denoting the foreground color or null if the * color should be cleared. */ public void setForegroundColor (NodeColor c) { checkDead(); String propVal = (c == null ? null : c.encode()); setProperty0 (PROP_KEY_FOREGROUND_COLOR, propVal); } /** * Sets the foreground color of this TreeNode. * * @param c object denoting the foreground color or null if the * color should be cleared. */ public void setBackgroundColor (NodeColor c) { checkDead(); String propVal = (c == null ? null : c.encode()); setProperty0 (PROP_KEY_BACKGROUND_COLOR, propVal); } /** * Returns the foreground color set of null, if no color is set. */ public NodeColor getForegroundColor () { checkDead(); String col = getProperty0 (PROP_KEY_FOREGROUND_COLOR); if (col == null) return null; return new NodeColor (col); } /** * Returns the foreground color set of null, if no color is set. */ public NodeColor getBackgroundColor () { checkDead(); String col = getProperty0 (PROP_KEY_BACKGROUND_COLOR); if (col == null) return null; return new NodeColor (col); } /** * Sets an auxiliary property. * * @param key the key of the property. Must not be a key of length 1 because * those are reserved. Must not be null. * @param value the value. If null, the property is cleared. */ public void setProperty (String key, String value) { checkDead(); if (isReservedPropertyKey(key)) throw new IllegalArgumentException(); setProperty0 (key, value); } /** * Returns an auxiliary property for a given key. * * @param key the key of the property. Must not be a key of length 1 because * those are reserved. Must not be null. * @return value of the property or null, if this property is not set. */ public String getProperty (String key) { checkDead(); if (isReservedPropertyKey(key)) throw new IllegalArgumentException(); return getProperty0(key); } /** * Sets an auxiliary property. * * @param key key of the property. Must not be null. * @param value value. May be null which clears the property. */ private void setProperty0 (String key, String value) { if (key == null) throw new IllegalArgumentException(); HashMap props = extractProps(); String oldVal = (String) props.get(key); if (eqProperty(oldVal, value)) return; if (value == null) props.remove(key); else props.put (key, value); String newPropString = props2String(props); tree_.handle (tree_.new ChangeNodeEvent(key_, text_, imageId_, newPropString, false)); } /** * Returns a property value for a given key or null, it this * property is not set. */ private String getProperty0 (String key) { if (key == null) throw new IllegalArgumentException(); if (props_ == null) return null; HashMap props = extractProps(); return (String) props.get(key); } /** * Returns true if and only if the two strings are equal with the * extension that two nulls are considered equal. */ static boolean eqProperty (String v1, String v2) { if (v1 == null) { return v2 == null; } else { if (v2 == null) return false; return v1.equals(v2); } } /** * Throws an exception if this is dead. */ private final void checkDead () { if (dead_) throw new IllegalStateException(); } /** * Returns the size of the hypothetical byte stream if this node * would be externalized. */ protected int streamedSize() { return 2 + text_.length() // text_ + 2 + key_.length() // key_ + 2 // imageId_ + 1 + (props_ == null ? 0 : props_.length()); // props_ } /** * Returns the key of the parent node of this if this node is an inner * node or null, if this node is a root. */ protected String getParentKey () { if (parent_ instanceof TreeNode) return ((TreeNode)parent_).key_; else return null; } /** * This method makes a copy of the subtree rooted at this and returns * it. The subtree is a perfect deep copy. The tree_ instance * in the copied subtree is the same as in the original one. * * @param parent the new parent of the returned TreeNode. */ protected TreeNode cloneSubTree (Object parent) { TreeNode n2 = new TreeNode(); n2.text_ = text_; n2.key_ = key_; n2.imageId_ = imageId_; n2.props_ = props_; n2.tree_ = tree_; n2.parent_ = parent; if (childs_ != null) { n2.childs_ = new ArrayList(); for (int i=0; itext_, key_ and imageId_ * from the stream. */ protected void internalize (XmaInput in) throws IOException, ClassNotFoundException { text_ = in.readString(); key_ = in.readString(); imageId_ = in.readShort(); boolean propsIsNull = in.readBoolean(); if (propsIsNull) props_ = null; else props_ = in.readString(); } /** * Expects a string of the form p1=v1,p2=v2... in instance variable * props_ and converts this information in a newly created HashMap * { (p1,v1), (p2,v2), ... }. * * @return newly created Hashmap */ private HashMap extractProps () { return string2Props (props_); } /** * Converts string encoded properties to newly created HashMap */ static HashMap string2Props (String s) { HashMap result = new HashMap(); if (s != null) { StringTokenizer tokizer = new StringTokenizer (s, ","); while (tokizer.hasMoreTokens()) { String kv = tokizer.nextToken(); int indexOfEquals = kv.indexOf('='); String key = kv.substring(0, indexOfEquals); String value = kv.substring(indexOfEquals+1); result.put(StringUtil.decode(key, '%'), StringUtil.decode(value, '%')); } } return result; } /** * Expects String tuples (p1,v1), (p2,v2), ... in props, converts this * information to a string following the format p1=v1,p2=v2. If props==null or * props.size()==0, null is returned. * * @param props HashMap containing String-pairs. */ static String props2String (HashMap props) { String result = null; if (props == null || props.size() == 0) { } else { StringBuffer buf = new StringBuffer(); Iterator iter = props.entrySet().iterator(); boolean first = true; while (iter.hasNext()) { Map.Entry kv = (Map.Entry) iter.next(); if (first) first = false; else buf.append(','); buf.append (StringUtil.encode((String)kv.getKey(), '%')); buf.append ("="); buf.append (StringUtil.encode((String)kv.getValue(), '%')); } result = buf.toString(); } return result; } /** * Returns true if s denotes a reserved property key. */ public static boolean isReservedPropertyKey (String s) { if (s == null) throw new IllegalArgumentException (); return s.length()==1; } /** * Shows if this node supports the lazy loading of child nodes. * The lazy loading is implemented by adding a dummynode to this node and * so making a childless node expandable. * The dummynode is removed before the expand event at the client side, * the isLazy() returns true in the expand event but is set to false after the first expand event occured on this node. * @return true if a node is lazy, i.e. expandable without real child nodes. */ public boolean isLazy() { return hasDummyNode() || lazy_; } /** * Sets the lazy flag. * Used by TreeUIDelegateClient. * @param lazy_ The lazy_ to set. * @author s3460 */ void setLazy(boolean lazy) { this.lazy_ = lazy; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy