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

at.spardat.xma.mdl.tree.TreeWM 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: TreeWM.java 10268 2013-01-11 10:34:57Z dschwarz $
package at.spardat.xma.mdl.tree;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import at.spardat.xma.mdl.ModelChangeEvent;
import at.spardat.xma.mdl.NewModelEvent;
import at.spardat.xma.mdl.NewModelEventFactory;
import at.spardat.xma.mdl.Notification;
import at.spardat.xma.mdl.WModel;
import at.spardat.xma.mdl.util.DNode;
import at.spardat.xma.mdl.util.Descriptive;
import at.spardat.xma.mdl.util.TransStringSet;
import at.spardat.xma.mdl.util.TransStringSet1;
import at.spardat.xma.mdl.util.TransStringSetN;
import at.spardat.xma.page.Page;
import at.spardat.xma.serializer.XmaInput;
import at.spardat.xma.serializer.XmaOutput;
import at.spardat.xma.test.TestUtil;
import at.spardat.xma.util.Assert;


/**
 * A widget model that holds a tree and its selection status.
 *
 * @author YSD, 03.05.2003 19:56:56
 */
public class TreeWM extends WModel implements ITreeWM {


    /**
     * Constructor
     *
     * @param id id of the widget model within the page
     * @param pm the page that this widget model is part of
     * @param style bit or combination of style constants beginning with S_
     *         in this class or a superclass.
     */
    public TreeWM (short id, Page pm, int style) {
        super (id, pm);
        if ((style & S_MULTI_SELECT) != 0) selection_ = new TransStringSetN();
        else selection_ = new TransStringSet1();
        page_ = pm;
    }

    /**
     * @see at.spardat.xma.mdl.Transactional#changed()
     */
    public boolean changed() {
        return copy_ != null || selection_.changed();
    }

    /**
     * @see at.spardat.xma.mdl.Transactional#rollback()
     */
    public void rollback() {
        if (copy_ != null) {
            // restore from copy_
            roots_ = copy_;
            recalcStreamedSize();
            recalcNodes();
            copy_ = null;
        }
        // discard changes
        changes_ = null;
        streamedChangesSize_ = 0;
        // rollback selection
        selection_.rollback();
        // notify listeners
        handle (new TreeChangedEvent());
        handle (new SelectionChangedEvent());
        if (Assert.ON) debugInvariant();
    }

    /**
     * @see at.spardat.xma.mdl.Transactional#commit()
     */
    public void commit() {
        copy_ = null;
        // discard changes
        changes_ = null;
        streamedChangesSize_ = 0;
        // commit selection
        selection_.commit();
        if (Assert.ON) debugInvariant();
    }

    /**
     * @see at.spardat.xma.mdl.WModel#handle(at.spardat.xma.mdl.ModelChangeEvent)
     */
    public boolean handle (ModelChangeEvent event) {
        if (Assert.ON) debugInvariant();
        if (event instanceof Notification) return true;   // would not change this
        if (!(event instanceof TreeChangeEvent)) {
            // for events that do not modify the tree structurally, we do not need
            // the change tracking stuff
            boolean success = event.execute();
            if (Assert.ON) debugInvariant();
            return success;
        }
        // make a copy of data_ if not copied yet
        boolean firstModification = false;  // first modification since last syncpoint?
        if (copy_ == null) {
            ensureSaved();
            firstModification = true;
        }
        // execute the event
        boolean success = event.execute();
        if (!success && firstModification) {
            copy_ = null;
        }
        if (!success) return false;
        // if this has been the first modification, decide if changes should be tracked
        if (firstModification) {
            if (streamedSize_ > 50) {
                // start change tracking if there are at least 50 bytes in the tree (heuristics)
                changes_ = new ArrayList();
                streamedChangesSize_ = 0;
            }
        }
        // if change tracking is on (changes_ != null), add the change to the list of changes
        if (changes_ != null && event instanceof TreeChangeEvent) {
            TreeChangeEvent tce = (TreeChangeEvent) event;
            changes_.add(event);
            streamedChangesSize_ += tce.streamedSize();
            // decide if change tracking should be stopped because the cumulated size of
            // the changes reaches some fraction of the cumulated size of the table.
            if (streamedChangesSize_ > streamedSize_ * MAX_STREAMED_CHANGES_RATIO) {
                // drop change list
                changes_ = null;
                streamedChangesSize_ = 0;
            }
        }
        if (Assert.ON) debugInvariant();
        return success;
    }

    /**
     * @see at.spardat.xma.mdl.WModel#clear()
     */
    public void clear() {
        handle (new ClearEvent(false));
    }

    /**
     * Returns the number of nodes in this tree.
     */
    public int size () {
        return nodes_.size();
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#select(java.lang.String)
     */
    public void select (String key) {
        if (nodes_.containsKey(key)) {
            boolean success = selection_.add (key);
            handle (new SelectionChangedEvent ());
        }
    }

    /**
     * @see at.spardat.xma.mdl.tree.ITreeWM#selectAll()
     */
    public void selectAll() {
        if (!isMultiSelect()) throw new IllegalStateException();
        Iterator        iter = nodes_.keySet().iterator();
        while (iter.hasNext()) {
            String key = (String) iter.next();
            selection_.add(key);
        }
        handle (new SelectionChangedEvent());
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#deselect(java.lang.String)
     */
    public void deselect (String key) {
        selection_.remove (key);
        handle (new SelectionChangedEvent ());
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#deselectAll()
     */
    public void deselectAll() {
        selection_.clear();
        handle (new SelectionChangedEvent ());
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#isMultiSelect()
     */
    public boolean isMultiSelect() {
        return selection_ instanceof TransStringSetN;
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#getSelected()
     */
    public String getSelected() {
        return selection_.getSome();
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#getSelection()
     */
    public String[] getSelection() {
        return selection_.getAll();
    }

    /**
     * @see at.spardat.xma.mdl.tree.ITreeWM#getSelectionOrdered()
     */
    public String[] getSelectionOrdered() {
        String[] result = new String[selection_.size()];
        if(result.length==1) {
            result[0]=selection_.getSome();
        } else if(result.length>1) {
            int i=0;
            for(Iterator it=roots_.iterator();it.hasNext();) {
                i=getSelectionOrdered((TreeNode) it.next(),result,i);
            }
        }
        return result;
    }

    /**
     * Adds all selected nodes of the given subtree to result beginning at index.
     * @param node root of the subtree to check
     * @param result to add the selected nodes
     * @param index current index where to add the next seleted node
     * @return the new index incremented by the count of found selected nodes
     */
    private int getSelectionOrdered(TreeNode node,String[] result,int index) {
        if(selection_.contains(node.key_)) {
            result[index++]=node.key_;
        }
        if(node.childs_!=null) {
            for(Iterator it=node.childs_.iterator();it.hasNext();) {
                index = getSelectionOrdered((TreeNode)it.next(),result,index);
            }
        }
        return index;
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#getSelectionCount()
     */
    public int getSelectionCount() {
        return selection_.size();
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#isSelected(java.lang.String)
     */
    public boolean isSelected(String key) {
        return selection_.contains(key);
    }

    /**
     * @see at.spardat.xma.mdl.ISelectable#isStrict()
     */
    public boolean isStrict() {
        return true;
    }


    /**
     * @see at.spardat.xma.mdl.WModel#randomlyChange()
     */
    public void randomlyChange() {
        if (Assert.ON) {
            debugInvariant();
            if (TestUtil.draw(0.05)) {
                // clear the table with low probability
                clear();
            } else if (TestUtil.draw(0.05)) {
                // remove a node
                if (size() > 0) {
                    getNode (randomKey()).remove();
                }
            } else {
                int change = TestUtil.randomInt(0, 8);
                switch (change) {
                    case 0:
                        // select a random entry
                        if (size() > 0) {
                            if (!isMultiSelect() || TestUtil.draw(0.8)) select(randomKey());
                            else selectAll();
                        }
                        break;
                    case 1:
                        // deselect a random entry
                        if (size() > 0) deselect (randomKey());
                        break;
                    case 2:
                        // deselect all
                        deselectAll();
                        break;
                    case 3:
                        // select a key randomly choosen
                        select (TestUtil.randomString(TestUtil.randomInt(0, 3)));
                        break;
                    case 4:
                    case 5:
                    case 6:
                        // randomly add a node
                        Object parent = this;
                        if (size() > 0) {
                            if (TestUtil.draw(0.8)) parent = getNode (randomKey());
                        }
                        String key = TestUtil.randomString(3);
                        if (!containsKey(key)) {
                            if (TestUtil.draw(0.5)) {
                                new TreeNode (parent, getChildCount(parent), key, TestUtil.randomString(1, 10), (short)TestUtil.randomInt(0, 3));
                            } else {
                                new TreeNode (parent, key, TestUtil.randomString(1, 10), (short)TestUtil.randomInt(0, 3));
                            }
                        }
                        break;
                    case 7:
                        // update a node
                        if (size() > 0) {
                            TreeNode n = getNode (randomKey());
                            n.setText(TestUtil.randomString(1, 10));
                        }
                        break;
                    case 8:
                        // change node color
                        if (size() > 0) {
                            TreeNode n = getNode (randomKey());
                            if (TestUtil.draw(0.7))
                                n.setForegroundColor(new NodeColor(TestUtil.randomInt(0, 255), TestUtil.randomInt(0, 255), TestUtil.randomInt(0, 255)));
                            if (TestUtil.draw(0.7))
                                n.setBackgroundColor(new NodeColor(TestUtil.randomInt(0, 255), TestUtil.randomInt(0, 255), TestUtil.randomInt(0, 255)));
                        }
                        break;

                }
            }
            debugInvariant();
        }
    }

    /**
     * Returns the number childs the parent has.
     *
     * @param parent the tree or a tree node
     */
    private int getChildCount (Object parent) {
        if (parent instanceof TreeNode) return ((TreeNode)parent).getChildCount();
        else return roots_.size();
    }

    // returns a randomly choosen key from this or null, if the tree is empty
    // just for testing, O(n)
    private String randomKey () {
        if (Assert.ON) {
            if (nodes_.size() == 0) return null;
            int index = TestUtil.randomInt(0, nodes_.size()-1);
            Iterator iter = nodes_.keySet().iterator();
            for (int i=0; io
    private void externalizeTree (XmaOutput o) throws IOException {
        // externalize parent nodes first
        o.writeShort("numRoots", roots_.size());
        for (int i=0, size=roots_.size(); iroots_ is empty.
     * Does not update nodes_ or any other instance variable.
     */
    private void internalizeTree (XmaInput in) throws IOException, ClassNotFoundException {
        short rootCount = in.readShort();
        for (int i=0; idead. It may no longer be accessed from outside.
     * It also removes all subnodes from nodes_. Furthermore,
     * it removes from selection_ any keys of the removed nodes
     * if correctSelection is true.
     */
    private void removeSubtree (TreeNode n, boolean correctSelection) {
        // first, recusively descend
        if (n.childs_ != null) {
            for (int i=0, size=n.childs_.size(); icopy_,
     * this method does a deep copy of the treenodes in roots_
     * and stores it in copy_.
     */
    private void ensureSaved () {
        if (copy_ == null) {
            copy_ = new ArrayList();
            for (int i=0, size=roots_.size(); istreamedSize_.
     */
    private void recalcStreamedSize () {
        streamedSize_ = 0;
        for (int i=0, size=roots_.size(); in including n itself.
     */
    private int streamedSizeOfSubtree (TreeNode n) {
        int result = n.streamedSize();
        if (n.childs_ != null) {
            for (int i=0, size=n.childs_.size(); inodes_, iterates over the tree and inserts
     * all tree nodes into nodes_.
     */
    private void recalcNodes () {
        nodes_.clear();
        for (int i=0, size=roots_.size(); in into nodes_.
     */
    private void recalcNodesInSubtree (TreeNode n) {
        if (n.childs_ != null) {
            for (int i=0, size=n.childs_.size(); in
     */
    private int nodeCountInSubtree (TreeNode n) {
        int ret = 1;
        if (n.childs_ != null) {
            for (int i=0; iTreeNode objects, keys are Strings (the keys).
     * This HashMap holds all nodes in the tree.
     */
    HashMap                         nodes_ = new HashMap();

    /**
     * The cumulated hypothetical streamed size of all treenodes
     */
    private int                     streamedSize_;

    /**
     * Contains a deep copy of roots_ in the case that this has been modified.
     * This instance variable is null, if this has not been modified.
     */
    private ArrayList               copy_;

    /**
     * Keeps track of changes (TreeChangeEvents) applied to this since the last
     * synchronization point (construction, commit, rollback). If there has
     * been no change yet (copy_ == null), then changes_ also is null.
     * If copy_ != null, changes_ may either be null or not. If changes_ == null,
     * the change list has been dropped because is has grown to large
     * in relation to the tree data. If changes_ != null, changes must be stored in this
     * instance variable until the cumulated streamed size of the changes exceeds
     * some fraction of the streamed size of the tree. Thereafter it may be dropped.
     */
    private ArrayList               changes_;

    /**
     * The cumulated streamed size of all changes in changes_.
     */
    private int                     streamedChangesSize_;

    /**
     * When the cumulated streamed size of the changes exceeds the cumulated
     * streamed size of the tree multiplied with this fraction, the
     * change list is dropped because serializing the complete tree
     * requires not much more space than serializing the deltas and it's of
     * no use to further track deltas (which would consume memory).
     */
    private static final double     MAX_STREAMED_CHANGES_RATIO = 0.7;

    /**
     * The page this widget model belongs to
     */
    private Page                    page_;





    private static final byte                T_CHANGENODE_EVT = 0;
    private static final byte                T_REMOVENODE_EVT = 1;
    private static final byte                T_ADDNODE_EVT = 2;
    private static final byte                T_CLEAR_EVT = 3;

    /**
     * Factory method for events used when internalizing events from a stream
     *
     * @param type one of the T_* codes defined above
     * @return newly contructed TreeChangeEvent;
     */
    private TreeChangeEvent createEvent (byte type) {
        switch (type) {
            case T_CHANGENODE_EVT:   return new ChangeNodeEvent(false);
            case T_REMOVENODE_EVT:   return new RemoveNodeEvent(false);
            case T_ADDNODE_EVT:      return new AddNodeEvent(false);
            case T_CLEAR_EVT:        return new ClearEvent(false);
            default: return null;
        }
    }



    /**
     * Common base class for all tree change events. When the events are executed,
     * they must keep the instance variables nodes_, roots_
     * and streamedSize_ up to date. The other instance variables
     * of the tree are controlled outside.
     *
     * @author YSD, 03.05.2003 22:48:42
     */
    abstract class TreeChangeEvent extends ModelChangeEvent implements Descriptive {

        /**
         * Constructor
         */
        public TreeChangeEvent (boolean fromUI) {
            super (TreeWM.this, fromUI);
        }

        /**
         * Returns a type indicator used in serialization
         */
        public abstract byte getType ();

        /**
         * Returns the number of bytes this event requires when streamed to a byte array
         */
        public abstract int streamedSize ();

        /**
         * Reads the instance variables from an input stream
         */
        public abstract void internalize (XmaInput in) throws IOException, ClassNotFoundException;

        /**
         * Writes the state to an output stream
         */
        public abstract void externalize (XmaOutput out) throws IOException;

        /**
         * @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
         */
        public void describe (DNode n) {
            n.appShortClass(this).app(": ");
            n.app("streamedSize", streamedSize());
        }
    }



    /**
     * Carries the information of a node change and executes it
     *
     * @author YSD, 03.05.2003 22:30:12
     */
    class ChangeNodeEvent extends TreeChangeEvent {

        // Constructor used before internalize
        public ChangeNodeEvent (boolean fromUI) {
            super (fromUI);
        }

        /**
         * Constructor
         */
        public ChangeNodeEvent (String key, String text, short imageId, String props, boolean fromUI){
            super (fromUI);
            key_ = key;
            text_ = text;
            imageId_ = imageId;
            props_ = props;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            TreeWM tree = TreeWM.this;
            TreeNode n = tree.getNode (key_);
            if (n == null) return false;
            streamedSize_ -= n.streamedSize();
            n.text_ = text_;
            n.imageId_ = imageId_;
            n.props_ = props_;
            streamedSize_ += n.streamedSize();
            return true;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#getType()
         */
        public byte getType() {
            return T_CHANGENODE_EVT;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#externalize(at.spardat.xma.mdl.XmaOutput)
         */
        public void externalize (XmaOutput o) throws IOException {
            o.writeString("key", key_);
            o.writeString("txt", text_);
            o.writeShort("image", imageId_);
            o.writeBoolean("jnprops", props_ == null);
            if (props_ != null) o.writeString ("props", props_);
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#internalize(at.spardat.xma.mdl.XmaInput, List)
         */
        public void internalize (XmaInput i) throws IOException, ClassNotFoundException {
            key_ = i.readString();
            text_ = i.readString();
            imageId_ = i.readShort();
            boolean     propsIsNull = i.readBoolean();
            if (propsIsNull) props_ = null;
            else props_ = i.readString();
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#streamedSize()
         */
        public int streamedSize() {
            return 1 + 4 + key_.length() + text_.length() + 2 + 1 + (props_ == null ? 0 : props_.length());
        }

        /**
         * @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
         */
        public void describe (DNode n) {
            super.describe(n); n.comma();
            n.app("key", key_).comma();
            n.app("text", text_).comma();
            n.app("imageId", imageId_).comma();
            n.app("props", props_);
        }

        /**
         * Returns the key of the target node
         */
        public String getKey () {
            return key_;
        }


        // key, not null
        private String              key_;

        // text, not null
        private String              text_;

        private short               imageId_;

        // encoded properties; may be null
        private String              props_;
    }


    /**
     * Carries the information of a node addition and is able to execute the event
     *
     * @author YSD, 03.05.2003 22:30:12
     */
    class AddNodeEvent extends TreeChangeEvent {

        // Constructor used before internalize
        public AddNodeEvent (boolean fromUI) {
            super (fromUI);
        }

        /**
         * Constructor
         *
         * @param toFill a preconstructed TreeNode which is used and initialized; may be null,
         *         then a new TreeNode is created.
         * @param parentKey is null if the new node is going to be a root
         */
        public AddNodeEvent (TreeNode toFill, String key, String parentKey, int index, String text, short imageId, boolean fromUI){
            super (fromUI);
            key_ = key;
            parentKey_ = parentKey;
            index_ = index;
            text_ = text;
            imageId_ = imageId;
            toFill_ = toFill;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            TreeWM tree = TreeWM.this;
            TreeNode parentNode = null;
            if (parentKey_ != null) parentNode = tree.getNode (parentKey_);
            TreeNode newNode = toFill_;
            if (newNode == null) newNode = new TreeNode();
            newNode.key_ = key_;
            newNode.text_ = text_;
            newNode.imageId_ = imageId_;
            newNode.tree_ = tree;
            if (parentKey_ == null) {
                // new root
                roots_.add(index_, newNode);
                newNode.parent_ = tree;
            } else {
                // new son of parentNode
                if (parentNode.childs_ == null) parentNode.childs_ = new ArrayList();
                parentNode.childs_.add(index_, newNode);
                newNode.parent_ = parentNode;
            }
            streamedSize_ += newNode.streamedSize();
            nodes_.put (newNode.key_, newNode);
            return true;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#getType()
         */
        public byte getType() {
            return T_ADDNODE_EVT;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#externalize(at.spardat.xma.mdl.XmaOutput)
         */
        public void externalize (XmaOutput o) throws IOException {
            o.writeString("key", key_);
            o.writeBoolean ("hasParent", parentKey_ == null);
            if (parentKey_ != null) o.writeString ("parent", parentKey_);
            o.writeInt("index", index_);
            o.writeString("text", text_);
            o.writeShort("image", imageId_);
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#internalize(at.spardat.xma.mdl.XmaInput, List)
         */
        public void internalize (XmaInput i) throws IOException, ClassNotFoundException {
            key_ = i.readString();
            if (!i.readBoolean()) parentKey_ = i.readString();
            index_ = i.readInt();
            text_ = i.readString();
            imageId_ = i.readShort();
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#streamedSize()
         */
        public int streamedSize() {
            return 1 + 2 + 1 + (parentKey_ == null ? 0 : parentKey_.length()) +
                   4 + 2 + text_.length() + 2;
        }

        /**
         * @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
         */
        public void describe (DNode n) {
            super.describe(n); n.comma();
            n.app("key", key_).comma();
            n.app("parentKey", parentKey_).comma();
            n.app("index", index_).comma();
            n.app("text", text_).comma();
            n.app("imageId", imageId_);
        }

        /**
         * Returns the key of the added node
         */
        public String getKey () {
            return key_;
        }

        /**
         * Returns the index where the new node is inserted in the parents list of childs.
         */
        public int getIndex () {
            return index_;
        }

        // key, not null
        private String              key_;

        // key of the parent node or null if new node is root
        private String              parentKey_;

        // the index where to insert the node
        private int                 index_;

        // text, not null
        private String              text_;

        private short               imageId_;

        private TreeNode            toFill_;
    }


    /**
     * Carries the information of a node deletion and has the capability to execute it.
     * This event not just deletes a given node, it deletes the complete subtree
     * that is rooted at the node. It also ajusts the selection.
     *
     * @author YSD, 03.05.2003 22:30:12
     */
    class RemoveNodeEvent extends TreeChangeEvent {

        // Constructor used before internalize
        public RemoveNodeEvent (boolean fromUI) {
            super (fromUI);
        }

        /**
         * Constructor
         *
         * @param parentKey is null if the new node is going to be a root
         */
        public RemoveNodeEvent (String key, boolean fromUI){
            super (fromUI);
            key_ = key;
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            TreeWM tree = TreeWM.this;
            TreeNode parentNode = null;
            TreeNode toDelete = tree.getNode(key_);
            streamedSize_ -= streamedSizeOfSubtree (toDelete);
            if (toDelete.parent_ instanceof TreeNode) parentNode = (TreeNode) toDelete.parent_;
            // parentNode is null if toDelete is a root
            tree.removeSubtree (toDelete, true);
            if (parentNode == null) {
                // delete root
                tree.roots_.remove(toDelete);
            } else {
                // delete some inner node
                parentNode.childs_.remove(toDelete);
                if (parentNode.childs_.size() == 0) parentNode.childs_ = null;
            }
            // correct selection
            selection_.remove(key_);
            return true;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#getType()
         */
        public byte getType() {
            return T_REMOVENODE_EVT;
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#externalize(at.spardat.xma.mdl.XmaOutput)
         */
        public void externalize (XmaOutput o) throws IOException {
            o.writeString("key", key_);
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#internalize(at.spardat.xma.mdl.XmaInput, List)
         */
        public void internalize (XmaInput i) throws IOException, ClassNotFoundException {
            key_ = i.readString ();
        }

        /**
         * @see at.spardat.xma.mdl.tree.TreeWM.TreeChangeEvent#streamedSize()
         */
        public int streamedSize() {
            return 1 + 2 + key_.length();
        }

        /**
         * @see at.spardat.xma.mdl.util.Descriptive#describe(at.spardat.xma.mdl.util.DNode)
         */
        public void describe (DNode n) {
            super.describe(n); n.comma();
            n.app("key", key_);
        }

        /**
         * Returns the key of the target node
         */
        public String getKey () {
            return key_;
        }


        // key, not null
        private String              key_;
    }


    /**
     * Carries the information of a deletion of the complete tree.
     *
     * @author YSD, 03.05.2003 22:30:12
     */
    class ClearEvent extends TreeChangeEvent {

        /**
         * Constructor
         */
        public ClearEvent (boolean fromUI){
            super (fromUI);
        }

        /**
         * @see at.spardat.xma.mdl.ModelChangeEvent#execute()
         */
        public boolean execute() {
            TreeWM tree = TreeWM.this;
            for (int i=0; iS_
		 */
		public NewTreeWMEvent(int style) {
			this.style=style;
		}

		// see at.spardat.xma.mdl.NewModelEvent.getType()
        public byte getType() {
            return NewModelEventFactory.TreeWM;
        }

		// see at.spardat.xma.mdl.NewModelEvent.createModel()
        public WModel createModel(short id,Page page) {
    		return new TreeWM(id,page,style);
		}

		// see at.spardat.xma.mdl.NewModelEvent.serialize()
		public void serialize(XmaOutput out) throws IOException {
			super.serialize(out);
            out.writeInt("style",style);
        }

		// see at.spardat.xma.mdl.NewModelEvent.deserialize()
        public void deserialize(XmaInput in) throws IOException, ClassNotFoundException {
        	super.deserialize(in);
        	style=in.readInt();
        }

    }

    // see at.sparda.xma.mdl.WModel.createNewModelEvent()
	public NewModelEvent createNewModelEvent() {
		int style = isMultiSelect()?S_MULTI_SELECT:S_NULL;
		return new NewTreeWMEvent(style);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy