source: src/main/java/weka/classifiers/bayes/net/EditableBayesNet.java @ 9

Last change on this file since 9 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 81.3 KB
Line 
1package weka.classifiers.bayes.net;
2/*
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 */
17
18/*
19 * EditableBayesNet.java
20 *
21 */
22
23import java.io.Serializable;
24import java.io.StringReader;
25import java.util.StringTokenizer;
26
27import javax.xml.parsers.DocumentBuilderFactory;
28
29import org.w3c.dom.CharacterData;
30import org.w3c.dom.Document;
31import org.w3c.dom.Element;
32import org.w3c.dom.Node;
33import org.w3c.dom.NodeList;
34
35import weka.classifiers.bayes.BayesNet;
36import weka.classifiers.bayes.net.estimate.DiscreteEstimatorBayes;
37import weka.core.Attribute;
38import weka.core.FastVector;
39import weka.core.Instances;
40import weka.core.RevisionUtils;
41import weka.core.SerializedObject;
42import weka.estimators.Estimator;
43import weka.filters.Filter;
44import weka.filters.unsupervised.attribute.Reorder;
45
46
47/**
48 <!-- globalinfo-start -->
49 * Bayes Network learning using various search algorithms and quality measures.<br/>
50 * Base class for a Bayes Network classifier. Provides datastructures (network structure, conditional probability distributions, etc.) and facilities common to Bayes Network learning algorithms like K2 and B.<br/>
51 * <br/>
52 * For more information see:<br/>
53 * <br/>
54 * http://www.cs.waikato.ac.nz/~remco/weka.pdf
55 * <p/>
56 <!-- globalinfo-end -->
57 *
58 <!-- options-start -->
59 * Valid options are: <p/>
60 *
61 * <pre> -D
62 *  Do not use ADTree data structure
63 * </pre>
64 *
65 * <pre> -B &lt;BIF file&gt;
66 *  BIF file to compare with
67 * </pre>
68 *
69 * <pre> -Q weka.classifiers.bayes.net.search.SearchAlgorithm
70 *  Search algorithm
71 * </pre>
72 *
73 * <pre> -E weka.classifiers.bayes.net.estimate.SimpleEstimator
74 *  Estimator algorithm
75 * </pre>
76 *
77 <!-- options-end -->
78 *
79 * @author Remco Bouckaert (rrb@xm.co.nz)
80 * @version $Revision: 4899 $
81 */
82
83public class EditableBayesNet extends BayesNet {
84        /** for serialization */
85        static final long serialVersionUID = 746037443258735954L;
86
87        /** location of nodes, used for graph drawing * */
88        protected FastVector m_nPositionX;
89
90        protected FastVector m_nPositionY;
91
92        /** marginal distributions * */
93        protected FastVector m_fMarginP;
94
95        /** evidence values, used for evidence propagation * */
96        protected FastVector m_nEvidence;
97
98        /** standard constructor * */
99        public EditableBayesNet() {
100                super();
101                m_nEvidence = new FastVector(0);
102                m_fMarginP = new FastVector(0);
103                m_nPositionX = new FastVector();
104                m_nPositionY = new FastVector();
105                clearUndoStack();
106        } // c'tor
107
108        /** constructor, creates empty network with nodes based on the attributes in a data set */
109        public EditableBayesNet(Instances instances) {
110                try {
111                        if (instances.classIndex() < 0) {
112                                instances.setClassIndex(instances.numAttributes() - 1);
113                        }
114                        m_Instances = normalizeDataSet(instances);
115                } catch (Exception e) {
116                        e.printStackTrace();
117                }
118
119                int nNodes = getNrOfNodes();
120                m_ParentSets = new ParentSet[nNodes];
121                for (int i = 0; i < nNodes; i++) {
122                        m_ParentSets[i] = new ParentSet();
123                }
124                m_Distributions = new Estimator[nNodes][];
125                for (int iNode = 0; iNode < nNodes; iNode++) {
126                        m_Distributions[iNode] = new Estimator[1];
127                        m_Distributions[iNode][0] = new DiscreteEstimatorBayes(getCardinality(iNode), 0.5);
128                }
129
130                m_nEvidence = new FastVector(nNodes);
131                for (int i = 0; i < nNodes; i++) {
132                        m_nEvidence.addElement(-1);
133                }
134                m_fMarginP = new FastVector(nNodes);
135                for (int i = 0; i < nNodes; i++) {
136                        double[] P = new double[getCardinality(i)];
137                        m_fMarginP.addElement(P);
138                }
139
140                m_nPositionX = new FastVector(nNodes);
141                m_nPositionY = new FastVector(nNodes);
142                for (int iNode = 0; iNode < nNodes; iNode++) {
143                        m_nPositionX.addElement(iNode%10 * 50);
144                        m_nPositionY.addElement(((int)(iNode/10)) * 50);
145                }
146
147        } // c'tor
148
149        /** constructor, copies Bayesian network structure from a Bayesian network
150         * encapsulated in a BIFReader
151         */
152        public EditableBayesNet(BIFReader other) {
153                m_Instances = other.m_Instances;
154                m_ParentSets = other.getParentSets();
155                m_Distributions = other.getDistributions();
156
157                int nNodes = getNrOfNodes();
158                m_nPositionX = new FastVector(nNodes);
159                m_nPositionY = new FastVector(nNodes);
160                for (int i = 0; i < nNodes; i++) {
161                        m_nPositionX.addElement(other.m_nPositionX[i]);
162                        m_nPositionY.addElement(other.m_nPositionY[i]);
163                }
164                m_nEvidence = new FastVector(nNodes);
165                for (int i = 0; i < nNodes; i++) {
166                        m_nEvidence.addElement(-1);
167                }
168                m_fMarginP = new FastVector(nNodes);
169                for (int i = 0; i < nNodes; i++) {
170                        double[] P = new double[getCardinality(i)];
171                        m_fMarginP.addElement(P);
172                }
173                clearUndoStack();
174        } // c'tor
175
176        /**
177         * constructor that potentially initializes instances as well
178         *
179         * @param bSetInstances
180         *            flag indicating whether to initialize instances or not
181         */
182        public EditableBayesNet(boolean bSetInstances) {
183                super();
184                m_nEvidence = new FastVector(0);
185                m_fMarginP = new FastVector(0);
186                m_nPositionX = new FastVector();
187                m_nPositionY = new FastVector();
188                clearUndoStack();
189                if (bSetInstances) {
190                        m_Instances = new Instances("New Network", new FastVector(0), 0);
191                }
192        } // c'tor
193
194
195        /** Assuming a network structure is defined and we want to learn from data,
196         * the data set must be put if correct order first and possibly discretized/missing
197         * values filled in before proceeding to CPT learning.
198         * @param instances data set to learn from
199         * @exception Exception when data sets are not compatible, e.g., a variable is missing
200         * or a variable has different nr of values.
201         */
202        public void setData(Instances instances) throws Exception {
203                // sync order of variables
204                int [] order = new int [getNrOfNodes()];
205                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
206                        String sName = getNodeName(iNode);
207                        int nNode = 0;
208                        while (nNode < getNrOfNodes() && !sName.equals(instances.attribute(nNode).name())) {
209                                nNode++;
210                        }
211                        if (nNode >= getNrOfNodes()) {
212                                throw new Exception("Cannot find node named [[[" + sName + "]]] in the data");
213                        }
214                        order[iNode] = nNode;
215                }
216                Reorder reorderFilter = new Reorder();
217                reorderFilter.setAttributeIndicesArray(order);
218                reorderFilter.setInputFormat(instances);
219                instances = Filter.useFilter(instances, reorderFilter);
220                // filter using discretization/missing values filter
221                Instances newInstances = new Instances(m_Instances, 0);
222                if (m_DiscretizeFilter == null && m_MissingValuesFilter == null) {
223                        newInstances = normalizeDataSet(instances);
224                } else {
225                        for (int iInstance = 0; iInstance < instances.numInstances(); iInstance++) {
226                                newInstances.add(normalizeInstance(instances.instance(iInstance)));
227                        }
228                }
229                //sanity check
230                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
231                        if (newInstances.attribute(iNode).numValues() != getCardinality(iNode)) {
232                                throw new Exception("Number of values of node [[[" + getNodeName(iNode) + "]]] differs in (discretized) dataset." );
233                        }
234                }
235                // if we got this far, all is ok with the data set and
236                // we can replace data set of Bayes net
237                m_Instances = newInstances;
238        } // setData
239
240        /** returns index of node with given name, or -1 if no such node exists
241         * @param sNodeName name of the node to get index for
242         */
243        public int getNode2(String sNodeName) {
244                int iNode = 0;
245                while (iNode < m_Instances.numAttributes()) {
246                        if (m_Instances.attribute(iNode).name().equals(sNodeName)) {
247                                return iNode;
248                        }
249                        iNode++;
250                }
251                return -1;
252        } // getNode2
253
254        /** returns index of node with given name. Throws exception if no such node exists
255         * @param sNodeName name of the node to get index for
256         */
257        public int getNode(String sNodeName) throws Exception {
258                int iNode = getNode2(sNodeName);
259                if (iNode < 0) {
260                        throw new Exception("Could not find node [[" + sNodeName + "]]");
261                }
262                return iNode;
263        } // getNode
264
265        /**
266         * Add new node to the network, initializing instances, parentsets,
267         * distributions. Used for manual manipulation of the Bayesian network.
268         *
269         * @param sName
270         *            name of the node. If the name already exists, an x is appended
271         *            to the name
272         * @param nCardinality
273         *            number of values for this node
274         * @throws Exception
275         */
276        public void addNode(String sName, int nCardinality) throws Exception {
277                addNode(sName, nCardinality, 100 + getNrOfNodes() * 10, 100 + getNrOfNodes() * 10);
278        } // addNode
279
280        /** Add node to network at a given position, initializing instances, parentsets,
281         * distributions. Used for manual manipulation of the Bayesian network.
282         *
283         * @param sName
284         *            name of the node. If the name already exists, an x is appended
285         *            to the name
286         * @param nCardinality
287         *            number of values for this node
288         * @param nPosX x-coordiate of the position to place this node
289         * @param nPosY y-coordiate of the position to place this node
290         * @throws Exception
291         */
292        public void addNode(String sName, int nCardinality, int nPosX, int nPosY) throws Exception {
293                if (getNode2(sName) >= 0) {
294                        addNode(sName + "x", nCardinality);
295                        return ;
296                }
297                // update instances
298                FastVector values = new FastVector(nCardinality);
299                for (int iValue = 0; iValue < nCardinality; iValue++) {
300                        values.addElement("Value" + (iValue + 1));
301                }
302                Attribute att = new Attribute(sName, values);
303                m_Instances.insertAttributeAt(att, m_Instances.numAttributes());
304                int nAtts = m_Instances.numAttributes();
305                // update parentsets
306                ParentSet[] parentSets = new ParentSet[nAtts];
307                for (int iParentSet = 0; iParentSet < nAtts - 1; iParentSet++) {
308                        parentSets[iParentSet] = m_ParentSets[iParentSet];
309                }
310                parentSets[nAtts - 1] = new ParentSet();
311                m_ParentSets = parentSets;
312                // update distributions
313                Estimator[][] distributions = new Estimator[nAtts][];
314                for (int iNode = 0; iNode < nAtts - 1; iNode++) {
315                        distributions[iNode] = m_Distributions[iNode];
316                }
317                distributions[nAtts - 1] = new Estimator[1];
318                distributions[nAtts - 1][0] = new DiscreteEstimatorBayes(nCardinality, 0.5);
319                m_Distributions = distributions;
320                // update positions
321                m_nPositionX.addElement(nPosX);
322                m_nPositionY.addElement(nPosY);
323                // update evidence & margins
324                m_nEvidence.addElement(-1);
325                double[] fMarginP = new double[nCardinality];
326                for (int iValue = 0; iValue < nCardinality; iValue++) {
327                        fMarginP[iValue] = 1.0 / nCardinality;
328                }
329                m_fMarginP.addElement(fMarginP);
330                // update undo stack
331                if (m_bNeedsUndoAction) {
332                        addUndoAction(new AddNodeAction(sName, nCardinality, nPosX, nPosY));
333                }
334        } // addNode
335
336        /**
337         * Delete node from the network, updating instances, parentsets,
338         * distributions Conditional distributions are condensed by taking the
339         * values for the target node to be its first value. Used for manual
340         * manipulation of the Bayesian network.
341         *
342         * @param sName
343         *            name of the node. If the name does not exists an exception is
344         *            thrown
345         * @throws Exception
346         */
347        public void deleteNode(String sName) throws Exception {
348                int nTargetNode = getNode(sName);
349                deleteNode(nTargetNode);
350        } // deleteNode
351
352        /**
353         * Delete node from the network, updating instances, parentsets,
354         * distributions Conditional distributions are condensed by taking the
355         * values for the target node to be its first value. Used for manual
356         * manipulation of the Bayesian network.
357         *
358         * @param nTargetNode
359         *            index of the node to delete.
360         * @throws Exception
361         */
362        public void deleteNode(int nTargetNode) throws Exception {
363                // update undo stack
364                if (m_bNeedsUndoAction) {
365                        addUndoAction(new DeleteNodeAction(nTargetNode));
366                }
367                int nAtts = m_Instances.numAttributes() - 1;
368                int nTargetCard = m_Instances.attribute(nTargetNode).numValues();
369                // update distributions
370                Estimator[][] distributions = new Estimator[nAtts][];
371                for (int iNode = 0; iNode < nAtts; iNode++) {
372                        int iNode2 = iNode;
373                        if (iNode >= nTargetNode) {
374                                iNode2++;
375                        }
376                        Estimator[] distribution = m_Distributions[iNode2];
377                        if (m_ParentSets[iNode2].contains(nTargetNode)) {
378                                // condense distribution, use values for targetnode = 0
379                                int nParentCard = m_ParentSets[iNode2].getCardinalityOfParents();
380                                nParentCard = nParentCard / nTargetCard;
381                                Estimator[] distribution2 = new Estimator[nParentCard];
382                                for (int iParent = 0; iParent < nParentCard; iParent++) {
383                                        distribution2[iParent] = distribution[iParent];
384                                }
385                                distribution = distribution2;
386                        }
387                        distributions[iNode] = distribution;
388                }
389                m_Distributions = distributions;
390                // update parentsets
391                ParentSet[] parentSets = new ParentSet[nAtts];
392                for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
393                        int iParentSet2 = iParentSet;
394                        if (iParentSet >= nTargetNode) {
395                                iParentSet2++;
396                        }
397                        ParentSet parentset = m_ParentSets[iParentSet2];
398                        parentset.deleteParent(nTargetNode, m_Instances);
399                        for (int iParent = 0; iParent < parentset.getNrOfParents(); iParent++) {
400                                int nParent = parentset.getParent(iParent);
401                                if (nParent > nTargetNode) {
402                                        parentset.SetParent(iParent, nParent - 1);
403                                }
404                        }
405                        parentSets[iParentSet] = parentset;
406                }
407                m_ParentSets = parentSets;
408                // update instances
409                m_Instances.setClassIndex(-1);
410                m_Instances.deleteAttributeAt(nTargetNode);
411                m_Instances.setClassIndex(nAtts - 1);
412
413                // update positions
414                m_nPositionX.removeElementAt(nTargetNode);
415                m_nPositionY.removeElementAt(nTargetNode);
416                // update evidence & margins
417                m_nEvidence.removeElementAt(nTargetNode);
418                m_fMarginP.removeElementAt(nTargetNode);
419        } // deleteNode
420
421        /**
422         * Delete nodes with indexes in selection from the network, updating instances, parentsets,
423         * distributions Conditional distributions are condensed by taking the
424         * values for the target node to be its first value. Used for manual
425         * manipulation of the Bayesian network.
426         *
427         * @param nodes
428         *            array of indexes of nodes to delete.
429         * @throws Exception
430         */
431        public void deleteSelection(FastVector nodes) {
432                // sort before proceeding
433                for (int i = 0; i < nodes.size(); i++) {
434                        for (int j = i + 1; j < nodes.size(); j++) {
435                                if ((Integer) nodes.elementAt(i) > (Integer) nodes.elementAt(j)) {
436                                        int h = (Integer) nodes.elementAt(i);
437                                        nodes.setElementAt(nodes.elementAt(j), i);
438                                        nodes.setElementAt(h, j);
439                                }
440                        }
441                }
442                // update undo stack
443                if (m_bNeedsUndoAction) {
444                        addUndoAction(new DeleteSelectionAction(nodes));
445                }
446                boolean bNeedsUndoAction = m_bNeedsUndoAction;
447                m_bNeedsUndoAction = false;
448                try {
449                        for (int iNode = nodes.size() - 1; iNode >= 0; iNode--) {
450                                deleteNode((Integer) nodes.elementAt(iNode));
451                        }
452                } catch (Exception e) {
453                        e.printStackTrace();
454                }
455                m_bNeedsUndoAction = bNeedsUndoAction;
456        } // deleteSelection
457
458        /** XML helper function for selecting elements under a node with a given name
459         * @param item XMLNode to select items from
460         * @param sElement name of the element to return
461         */
462        FastVector selectElements(Node item, String sElement) throws Exception {
463                NodeList children = item.getChildNodes();
464                FastVector nodelist = new FastVector();
465                for (int iNode = 0; iNode < children.getLength(); iNode++) {
466                        Node node = children.item(iNode);
467                        if ((node.getNodeType() == Node.ELEMENT_NODE) && node.getNodeName().equals(sElement)) {
468                                nodelist.addElement(node);
469                        }
470                }
471                return nodelist;
472        } // selectElements
473
474        /**
475         * XML helper function. Returns all TEXT children of the given node in one string. Between the
476         * node values new lines are inserted.
477         *
478         * @param node
479         *            the node to return the content for
480         * @return the content of the node
481         */
482        public String getContent(Element node) {
483                NodeList list;
484                Node item;
485                int i;
486                String result;
487
488                result = "";
489                list = node.getChildNodes();
490
491                for (i = 0; i < list.getLength(); i++) {
492                        item = list.item(i);
493                        if (item.getNodeType() == Node.TEXT_NODE)
494                                result += "\n" + item.getNodeValue();
495                }
496
497                return result;
498        }
499
500        /** XML helper function that returns DEFINITION element from a XMLBIF document
501         * for a node with a given name.
502         * @param doc XMLBIF document
503         * @param sName name of the node to get the definition for
504         */
505        Element getDefinition(Document doc, String sName) throws Exception {
506                NodeList nodelist = doc.getElementsByTagName("DEFINITION");
507                for (int iNode = 0; iNode < nodelist.getLength(); iNode++) {
508                        Node node = nodelist.item(iNode);
509                        FastVector list = selectElements(node, "FOR");
510                        if (list.size() > 0) {
511                                Node forNode = (Node) list.elementAt(0);
512                                if (getContent((Element) forNode).trim().equals(sName)) {
513                                        return (Element) node;
514                                }
515                        }
516                }
517                throw new Exception("Could not find definition for ((" + sName + "))");
518        } // getDefinition
519
520
521        /** Paste modes. This allows for verifying that a past action does not cause
522         * any problems before actually performing the paste operation.
523         */
524        final static int TEST = 0;
525        final static int EXECUTE = 1;
526
527        /** Apply paste operation with XMLBIF fragment. This adds nodes in the XMLBIF fragment
528         * to the network, together with its parents. First, paste in test mode to verify
529         * no problems occur, then execute paste operation. If a problem occurs (e.g. parent
530         * does not exist) then a exception is thrown.
531         * @param sXML XMLBIF fragment to paste into the network
532         */
533        public void paste(String sXML) throws Exception {
534                try {
535                        paste(sXML, TEST);
536                } catch (Exception e) {
537                        throw e;
538                }
539                paste(sXML, EXECUTE);
540        } // paste
541
542        /** Apply paste operation with XMLBIF fragment. Depending on the paste mode, the
543         * nodes are actually added to the network or it is just tested that the nodes can
544         * be added to the network.
545         * @param sXML XMLBIF fragment to paste into the network
546         * @param mode paste mode TEST or EXECUTE
547         */
548        void paste(String sXML, int mode) throws Exception {
549                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
550                factory.setValidating(true);
551                Document doc = factory.newDocumentBuilder().parse(new org.xml.sax.InputSource(new StringReader(sXML)));
552                doc.normalize();
553
554                // create nodes first
555                NodeList nodelist = doc.getElementsByTagName("VARIABLE");
556                FastVector sBaseNames = new FastVector();
557                Instances instances = new Instances(m_Instances, 0);
558                int nBase = instances.numAttributes();
559                for (int iNode = 0; iNode < nodelist.getLength(); iNode++) {
560                        // Get element
561                        FastVector valueslist;
562                        // Get the name of the node
563                        valueslist = selectElements(nodelist.item(iNode), "OUTCOME");
564
565                        int nValues = valueslist.size();
566                        // generate value strings
567                        FastVector nomStrings = new FastVector(nValues + 1);
568                        for (int iValue = 0; iValue < nValues; iValue++) {
569                                Node node = ((Node) valueslist.elementAt(iValue)).getFirstChild();
570                                String sValue = ((CharacterData) (node)).getData();
571                                if (sValue == null) {
572                                        sValue = "Value" + (iValue + 1);
573                                }
574                                nomStrings.addElement(sValue);
575                        }
576                        FastVector nodelist2;
577                        // Get the name of the network
578                        nodelist2 = selectElements(nodelist.item(iNode), "NAME");
579                        if (nodelist2.size() == 0) {
580                                throw new Exception("No name specified for variable");
581                        }
582                        String sBaseName = ((CharacterData) (((Node) nodelist2.elementAt(0)).getFirstChild())).getData();
583                        sBaseNames.addElement(sBaseName);
584                        String sNodeName = sBaseName;
585                        if (getNode2(sNodeName) >= 0) {
586                                sNodeName = "Copy of " + sBaseName;
587                        }
588                        int iAttempt = 2;
589                        while (getNode2(sNodeName) >= 0) {
590                                sNodeName = "Copy (" + iAttempt + ") of " + sBaseName;
591                                iAttempt++;
592                        }
593
594                        Attribute att = new Attribute(sNodeName, nomStrings);
595                        instances.insertAttributeAt(att, instances.numAttributes());
596
597                        valueslist = selectElements(nodelist.item(iNode), "PROPERTY");
598                        nValues = valueslist.size();
599                        // generate value strings
600                        int nPosX = iAttempt * 10;
601                        int nPosY = iAttempt * 10;
602                        for (int iValue = 0; iValue < nValues; iValue++) {
603                                // parsing for strings of the form "position = (73, 165)"
604                                Node node = ((Node) valueslist.elementAt(iValue)).getFirstChild();
605                                String sValue = ((CharacterData) (node)).getData();
606                                if (sValue.startsWith("position")) {
607                                        int i0 = sValue.indexOf('(');
608                                        int i1 = sValue.indexOf(',');
609                                        int i2 = sValue.indexOf(')');
610                                        String sX = sValue.substring(i0 + 1, i1).trim();
611                                        String sY = sValue.substring(i1 + 1, i2).trim();
612                                        try {
613                                                nPosX = (Integer.parseInt(sX) + iAttempt * 10);
614                                                nPosY = (Integer.parseInt(sY) + iAttempt * 10);
615                                        } catch (NumberFormatException e) {
616                                                System.err.println("Wrong number format in position :(" + sX + "," + sY + ")");
617                                        }
618                                }
619                        }
620                        if (mode == EXECUTE) {
621                                m_nPositionX.addElement(nPosX);
622                                m_nPositionY.addElement(nPosY);
623                        }
624
625                }
626
627                FastVector nodelist2;
628                Estimator[][] distributions = new Estimator[nBase + sBaseNames.size()][];
629                ParentSet[] parentsets = new ParentSet[nBase + sBaseNames.size()];
630                for (int iNode = 0; iNode < nBase; iNode++) {
631                        distributions[iNode] = m_Distributions[iNode];
632                        parentsets[iNode] = m_ParentSets[iNode];
633                }
634                if (mode == EXECUTE) {
635                        m_Instances = instances;
636                }
637                // create arrows & create distributions
638                for (int iNode = 0; iNode < sBaseNames.size(); iNode++) {
639                        // find definition that goes with this node
640                        String sName = (String) sBaseNames.elementAt(iNode);
641                        Element definition = getDefinition(doc, sName);
642                        parentsets[nBase + iNode] = new ParentSet();
643
644                        // get the parents for this node
645                        // resolve structure
646                        nodelist2 = selectElements(definition, "GIVEN");
647                        for (int iParent = 0; iParent < nodelist2.size(); iParent++) {
648                                Node parentName = ((Node) nodelist2.elementAt(iParent)).getFirstChild();
649                                String sParentName = ((CharacterData) (parentName)).getData();
650                                int nParent = -1;
651                                for (int iBase = 0; iBase < sBaseNames.size(); iBase++) {
652                                        if (sParentName.equals((String) sBaseNames.elementAt(iBase))) {
653                                                nParent = nBase + iBase;
654                                        }
655                                }
656                                if (nParent < 0) {
657                                        nParent = getNode(sParentName);
658                                }
659                                parentsets[nBase + iNode].addParent(nParent, instances);
660                        }
661                        // resolve conditional probability table
662                        int nCardinality = parentsets[nBase + iNode].getCardinalityOfParents();
663                        int nValues = instances.attribute(nBase + iNode).numValues();
664                        distributions[nBase + iNode] = new Estimator[nCardinality];
665                        for (int i = 0; i < nCardinality; i++) {
666                                distributions[nBase + iNode][i] = new DiscreteEstimatorBayes(nValues, 0.0f);
667                        }
668
669                        String sTable = getContent((Element) selectElements(definition, "TABLE").elementAt(0));
670                        sTable = sTable.replaceAll("\\n", " ");
671                        StringTokenizer st = new StringTokenizer(sTable.toString());
672
673                        for (int i = 0; i < nCardinality; i++) {
674                                DiscreteEstimatorBayes d = (DiscreteEstimatorBayes) distributions[nBase + iNode][i];
675                                for (int iValue = 0; iValue < nValues; iValue++) {
676                                        String sWeight = st.nextToken();
677                                        d.addValue(iValue, new Double(sWeight).doubleValue());
678                                }
679                        }
680                        if (mode == EXECUTE) {
681                                m_nEvidence.insertElementAt(-1, nBase + iNode);
682                                m_fMarginP.insertElementAt(new double[getCardinality(nBase + iNode)], nBase + iNode);
683                        }
684                }
685                if (mode == EXECUTE) {
686                        m_Distributions = distributions;
687                        m_ParentSets = parentsets;
688                }
689                // update undo stack
690                if (mode == EXECUTE && m_bNeedsUndoAction) {
691                        addUndoAction(new PasteAction(sXML, nBase));
692                }
693        } // paste
694
695        /**
696         * Add arc between two nodes Distributions are updated by duplication for
697         * every value of the parent node.
698         *
699         * @param sParent
700         *            name of the parent node
701         * @param sChild
702         *            name of the child node
703         * @throws Exception
704         *             if parent or child cannot be found in network
705         */
706        public void addArc(String sParent, String sChild) throws Exception {
707                int nParent = getNode(sParent);
708                int nChild = getNode(sChild);
709                addArc(nParent, nChild);
710        } // addArc
711
712        /**
713         * Add arc between two nodes Distributions are updated by duplication for
714         * every value of the parent node.
715         *
716         * @param nParent
717         *            index of the parent node
718         * @param nChild
719         *            index of the child node
720         * @throws Exception
721         */
722        public void addArc(int nParent, int nChild) throws Exception {
723                // update undo stack
724                if (m_bNeedsUndoAction) {
725                        addUndoAction(new AddArcAction(nParent, nChild));
726                }
727                int nOldCard = m_ParentSets[nChild].getCardinalityOfParents();
728                // update parentsets
729                m_ParentSets[nChild].addParent(nParent, m_Instances);
730                // update distributions
731                int nNewCard = m_ParentSets[nChild].getCardinalityOfParents();
732                Estimator[] ds = new Estimator[nNewCard];
733                for (int iParent = 0; iParent < nNewCard; iParent++) {
734                        ds[iParent] = Estimator.clone(m_Distributions[nChild][iParent % nOldCard]);
735                }
736                m_Distributions[nChild] = ds;
737        } // addArc
738
739        /**
740         * Add arc between parent node and each of the nodes in a given list.
741         * Distributions are updated as above.
742         *
743         * @param sParent
744         *            name of the parent node
745         * @param nodes
746         *            array of indexes of child nodes
747         * @throws Exception
748         */
749        public void addArc(String sParent, FastVector nodes) throws Exception {
750                int nParent = getNode(sParent);
751                // update undo stack
752                if (m_bNeedsUndoAction) {
753                        addUndoAction(new AddArcAction(nParent, nodes));
754                }
755                boolean bNeedsUndoAction = m_bNeedsUndoAction;
756                m_bNeedsUndoAction = false;
757                for (int iNode = 0; iNode < nodes.size(); iNode++) {
758                        int nNode = (Integer) nodes.elementAt(iNode);
759                        addArc(nParent, nNode);
760                }
761                m_bNeedsUndoAction = bNeedsUndoAction;
762        } // addArc
763
764        /**
765         * Delete arc between two nodes. Distributions are updated by condensing for
766         * the parent node taking its first value.
767         *
768         * @param sParent
769         *            name of the parent node
770         * @param sChild
771         *            name of the child node
772         * @throws Exception
773         *             if parent or child cannot be found in network
774         */
775        public void deleteArc(String sParent, String sChild) throws Exception {
776                int nParent = getNode(sParent);
777                int nChild = getNode(sChild);
778                deleteArc(nParent, nChild);
779        } // deleteArc
780
781        /**
782         * Delete arc between two nodes. Distributions are updated by condensing for
783         * the parent node taking its first value.
784         *
785         * @param nParent
786         *            index of the parent node
787         * @param nChild
788         *            index of the child node
789         * @throws Exception
790         */
791        public void deleteArc(int nParent, int nChild) throws Exception {
792                // update undo stack
793                if (m_bNeedsUndoAction) {
794                        addUndoAction(new DeleteArcAction(nParent, nChild));
795                }
796                // update distributions
797                // condense distribution, use values for targetnode = 0
798                int nParentCard = m_ParentSets[nChild].getCardinalityOfParents();
799                int nTargetCard = m_Instances.attribute(nChild).numValues();
800                nParentCard = nParentCard / nTargetCard;
801                Estimator[] distribution2 = new Estimator[nParentCard];
802                for (int iParent = 0; iParent < nParentCard; iParent++) {
803                        distribution2[iParent] = m_Distributions[nChild][iParent];
804                }
805                m_Distributions[nChild] = distribution2;
806                // update parentsets
807                m_ParentSets[nChild].deleteParent(nParent, m_Instances);
808        } // deleteArc
809
810
811        /** specify distribution of a node
812         * @param sName name of the node to specify distribution for
813         * @param P matrix representing distribution with P[i][j] = P(node = j | parent configuration = i)
814         * @throws Exception
815         *             if parent or child cannot be found in network
816         */
817        public void setDistribution(String sName, double[][] P) throws Exception {
818                int nTargetNode = getNode(sName);
819                setDistribution(nTargetNode, P);
820        } // setDistribution
821
822        /** specify distribution of a node
823         * @param nTargetNode index of the node to specify distribution for
824         * @param P matrix representing distribution with P[i][j] = P(node = j | parent configuration = i)
825         * @throws Exception
826         *             if parent or child cannot be found in network
827         */
828        public void setDistribution(int nTargetNode, double[][] P) throws Exception {
829                // update undo stack
830                if (m_bNeedsUndoAction) {
831                        addUndoAction(new SetDistributionAction(nTargetNode, P));
832                }
833                Estimator[] distributions = m_Distributions[nTargetNode];
834                for (int iParent = 0; iParent < distributions.length; iParent++) {
835                        DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(P[0].length, 0);
836                        for (int iValue = 0; iValue < distribution.getNumSymbols(); iValue++) {
837                                distribution.addValue(iValue, P[iParent][iValue]);
838                        }
839                        distributions[iParent] = distribution;
840                }
841                // m_Distributions[nTargetNode] = distributions;
842        } // setDistribution
843
844        /** returns distribution of a node in matrix form with matrix representing distribution
845         * with P[i][j] = P(node = j | parent configuration = i)
846         * @param sName name of the node to get distribution from
847         */
848        public double[][] getDistribution(String sName) {
849                int nTargetNode = getNode2(sName);
850                return getDistribution(nTargetNode);
851        } // getDistribution
852
853        /** returns distribution of a node in matrix form with matrix representing distribution
854         * with P[i][j] = P(node = j | parent configuration = i)
855         * @param nTargetNode index of the node to get distribution from
856         */
857        public double[][] getDistribution(int nTargetNode) {
858                int nParentCard = m_ParentSets[nTargetNode].getCardinalityOfParents();
859                int nCard = m_Instances.attribute(nTargetNode).numValues();
860                double[][] P = new double[nParentCard][nCard];
861                for (int iParent = 0; iParent < nParentCard; iParent++) {
862                        for (int iValue = 0; iValue < nCard; iValue++) {
863                                P[iParent][iValue] = m_Distributions[nTargetNode][iParent].getProbability(iValue);
864                        }
865                }
866                return P;
867        } // getDistribution
868
869        /** returns array of values of a node
870         * @param sName name of the node to get values from
871         */
872        public String[] getValues(String sName) {
873                int nTargetNode = getNode2(sName);
874                return getValues(nTargetNode);
875        } // getValues
876
877        /** returns array of values of a node
878         * @param nTargetNode index of the node to get values from
879         */
880        public String[] getValues(int nTargetNode) {
881                String[] values = new String[getCardinality(nTargetNode)];
882                for (int iValue = 0; iValue < values.length; iValue++) {
883                        values[iValue] = m_Instances.attribute(nTargetNode).value(iValue);
884                }
885                return values;
886        } // getValues
887
888        /** returns value of a node
889         * @param nTargetNode index of the node to get values from
890         * @param iValue index of the value
891         */
892        public String getValueName(int nTargetNode, int iValue) {
893                return m_Instances.attribute(nTargetNode).value(iValue);
894        } // getNodeValue
895
896        /** change the name of a node
897         * @param nTargetNode index of the node to set name for
898         * @param sName new name to assign
899         */
900        public void setNodeName(int nTargetNode, String sName) {
901                // update undo stack
902                if (m_bNeedsUndoAction) {
903                        addUndoAction(new RenameAction(nTargetNode, getNodeName(nTargetNode), sName));
904                }
905                Attribute att = m_Instances.attribute(nTargetNode);
906                int nCardinality = att.numValues();
907                FastVector values = new FastVector(nCardinality);
908                for (int iValue = 0; iValue < nCardinality; iValue++) {
909                        values.addElement(att.value(iValue));
910                }
911                replaceAtt(nTargetNode, sName, values);
912        } // setNodeName
913
914        /** change the name of a value of a node
915         * @param nTargetNode index of the node to set name for
916         * @param sValue current name of the value
917         * @param sNewValue new name of the value
918         */
919        public void renameNodeValue(int nTargetNode, String sValue, String sNewValue) {
920                // update undo stack
921                if (m_bNeedsUndoAction) {
922                        addUndoAction(new RenameValueAction(nTargetNode, sValue, sNewValue));
923                }
924                Attribute att = m_Instances.attribute(nTargetNode);
925                int nCardinality = att.numValues();
926                FastVector values = new FastVector(nCardinality);
927                for (int iValue = 0; iValue < nCardinality; iValue++) {
928                        if (att.value(iValue).equals(sValue)) {
929                                values.addElement(sNewValue);
930                        } else {
931                                values.addElement(att.value(iValue));
932                        }
933                }
934                replaceAtt(nTargetNode, att.name(), values);
935        } // renameNodeValue
936
937
938        /** Add node value to a node. Distributions for the node assign zero probability
939         * to the new value. Child nodes duplicate CPT conditioned on the new value.
940         * @param nTargetNode index of the node to add value for
941         * @param sNewValue name of the value
942         */
943        public void addNodeValue(int nTargetNode, String sNewValue) {
944                // update undo stack
945                if (m_bNeedsUndoAction) {
946                        addUndoAction(new AddValueAction(nTargetNode, sNewValue));
947                }
948                Attribute att = m_Instances.attribute(nTargetNode);
949                int nCardinality = att.numValues();
950                FastVector values = new FastVector(nCardinality);
951                for (int iValue = 0; iValue < nCardinality; iValue++) {
952                        values.addElement(att.value(iValue));
953                }
954                values.addElement(sNewValue);
955                replaceAtt(nTargetNode, att.name(), values);
956
957                // update distributions of this node
958                Estimator[] distributions = m_Distributions[nTargetNode];
959                int nNewCard = values.size();
960                for (int iParent = 0; iParent < distributions.length; iParent++) {
961                        DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nNewCard, 0);
962                        for (int iValue = 0; iValue < nNewCard - 1; iValue++) {
963                                distribution.addValue(iValue, distributions[iParent].getProbability(iValue));
964                        }
965                        distributions[iParent] = distribution;
966                }
967
968                // update distributions of all children
969                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
970                        if (m_ParentSets[iNode].contains(nTargetNode)) {
971                                distributions = m_Distributions[iNode];
972                                ParentSet parentSet = m_ParentSets[iNode];
973                                int nParentCard = parentSet.getFreshCardinalityOfParents(m_Instances);
974                                Estimator[] newDistributions = new Estimator[nParentCard];
975                                int nCard = getCardinality(iNode);
976                                int nParents = parentSet.getNrOfParents();
977                                int[] values2 = new int[nParents];
978                                int iOldPos = 0;
979                                int iTargetNode = 0;
980                                while (parentSet.getParent(iTargetNode) != nTargetNode) {
981                                        iTargetNode++;
982                                }
983                                for (int iPos = 0; iPos < nParentCard; iPos++) {
984                                        DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nCard, 0);
985                                        for (int iValue = 0; iValue < nCard; iValue++) {
986                                                distribution.addValue(iValue, distributions[iOldPos].getProbability(iValue));
987                                        }
988                                        newDistributions[iPos] = distribution;
989                                        // update values
990                                        int i = 0;
991                                        values2[i]++;
992                                        while (i < nParents && values2[i] == getCardinality(parentSet.getParent(i))) {
993                                                values2[i] = 0;
994                                                i++;
995                                                if (i < nParents) {
996                                                        values2[i]++;
997                                                }
998                                        }
999                                        if (values2[iTargetNode] != nNewCard - 1) {
1000                                                iOldPos++;
1001                                        }
1002                                }
1003                                m_Distributions[iNode] = newDistributions;
1004                        }
1005                }
1006        } // addNodeValue
1007
1008
1009        /** Delete node value from a node. Distributions for the node are scaled
1010         * up proportional to existing distribution
1011         * (or made uniform if zero probability is assigned to remainder of values).
1012        .* Child nodes delete CPTs conditioned on the new value.
1013         * @param nTargetNode index of the node to delete value from
1014         * @param sValue name of the value to delete
1015         */
1016        public void delNodeValue(int nTargetNode, String sValue) throws Exception {
1017                // update undo stack
1018                if (m_bNeedsUndoAction) {
1019                        addUndoAction(new DelValueAction(nTargetNode, sValue));
1020                }
1021                Attribute att = m_Instances.attribute(nTargetNode);
1022                int nCardinality = att.numValues();
1023                FastVector values = new FastVector(nCardinality);
1024                int nValue = -1;
1025                for (int iValue = 0; iValue < nCardinality; iValue++) {
1026                        if (att.value(iValue).equals(sValue)) {
1027                                nValue = iValue;
1028                        } else {
1029                                values.addElement(att.value(iValue));
1030                        }
1031                }
1032                if (nValue < 0) {
1033                        // could not find value
1034                        throw new Exception("Node " + nTargetNode + " does not have value (" + sValue + ")");
1035                }
1036                replaceAtt(nTargetNode, att.name(), values);
1037
1038                // update distributions
1039                Estimator[] distributions = m_Distributions[nTargetNode];
1040                int nCard = values.size();
1041                for (int iParent = 0; iParent < distributions.length; iParent++) {
1042                        DiscreteEstimatorBayes distribution = new DiscreteEstimatorBayes(nCard, 0);
1043                        double sum = 0;
1044                        for (int iValue = 0; iValue < nCard; iValue++) {
1045                                sum += distributions[iParent].getProbability(iValue);
1046                        }
1047                        if (sum > 0) {
1048                                for (int iValue = 0; iValue < nCard; iValue++) {
1049                                        distribution.addValue(iValue, distributions[iParent].getProbability(iValue) / sum);
1050                                }
1051                        } else {
1052                                for (int iValue = 0; iValue < nCard; iValue++) {
1053                                        distribution.addValue(iValue, 1.0 / nCard);
1054                                }
1055                        }
1056                        distributions[iParent] = distribution;
1057                }
1058
1059                // update distributions of all children
1060                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
1061                        if (m_ParentSets[iNode].contains(nTargetNode)) {
1062                                ParentSet parentSet = m_ParentSets[iNode];
1063                                distributions = m_Distributions[iNode];
1064                                Estimator[] newDistributions = new Estimator[distributions.length * nCard / (nCard + 1)];
1065                                int iCurrentDist = 0;
1066
1067                                int nParents = parentSet.getNrOfParents();
1068                                int[] values2 = new int[nParents];
1069                                // fill in the values
1070                                int nParentCard = parentSet.getFreshCardinalityOfParents(m_Instances) * (nCard + 1) / nCard;
1071                                int iTargetNode = 0;
1072                                while (parentSet.getParent(iTargetNode) != nTargetNode) {
1073                                        iTargetNode++;
1074                                }
1075                                int[] nCards = new int[nParents];
1076                                for (int iParent = 0; iParent < nParents; iParent++) {
1077                                        nCards[iParent] = getCardinality(parentSet.getParent(iParent));
1078                                }
1079                                nCards[iTargetNode]++;
1080                                for (int iPos = 0; iPos < nParentCard; iPos++) {
1081                                        if (values2[iTargetNode] != nValue) {
1082                                                newDistributions[iCurrentDist++] = distributions[iPos];
1083                                        }
1084                                        // update values
1085                                        int i = 0;
1086                                        values2[i]++;
1087                                        while (i < nParents && values2[i] == nCards[i]) {
1088                                                values2[i] = 0;
1089                                                i++;
1090                                                if (i < nParents) {
1091                                                        values2[i]++;
1092                                                }
1093                                        }
1094                                }
1095
1096                                m_Distributions[iNode] = newDistributions;
1097                        }
1098                }
1099                // update evidence
1100                if (getEvidence(nTargetNode) > nValue) {
1101                        setEvidence(nTargetNode, getEvidence(nTargetNode) - 1);
1102                }
1103        } // delNodeValue
1104
1105        /** set position of node
1106         * @param iNode index of node to set position for
1107         * @param nX x position of new position
1108         * @param nY y position of new position
1109         */
1110        public void setPosition(int iNode, int nX, int nY) {
1111                // update undo stack
1112                if (m_bNeedsUndoAction) {
1113                        boolean isUpdate = false;
1114                        UndoAction undoAction = null;
1115                        try {
1116                                if (m_undoStack.size() > 0) {
1117                                        undoAction = (UndoAction) m_undoStack.elementAt(m_undoStack.size() - 1);
1118                                        SetPositionAction posAction = (SetPositionAction) undoAction;
1119                                        if (posAction.m_nTargetNode == iNode) {
1120                                                isUpdate = true;
1121                                                posAction.setUndoPosition(nX, nY);
1122                                        }
1123                                }
1124                        } catch (Exception e) {
1125                                // ignore. it's not a SetPositionAction
1126                        }
1127                        if (!isUpdate) {
1128                                addUndoAction(new SetPositionAction(iNode, nX, nY));
1129                        }
1130                }
1131                m_nPositionX.setElementAt(nX, iNode);
1132                m_nPositionY.setElementAt(nY, iNode);
1133        } // setPosition
1134
1135        /** Set position of node. Move set of nodes with the same displacement
1136         * as a specified node.
1137         * @param nNode index of node to set position for
1138         * @param nX x position of new position
1139         * @param nY y position of new position
1140         * @param nodes array of indexes of nodes to move
1141         */
1142        public void setPosition(int nNode, int nX, int nY, FastVector nodes) {
1143                int dX = nX - getPositionX(nNode);
1144                int dY = nY - getPositionY(nNode);
1145                // update undo stack
1146                if (m_bNeedsUndoAction) {
1147                        boolean isUpdate = false;
1148                        try {
1149                                UndoAction undoAction = null;
1150                                if (m_undoStack.size() > 0) {
1151                                        undoAction = (UndoAction) m_undoStack.elementAt(m_undoStack.size() - 1);
1152                                                SetGroupPositionAction posAction = (SetGroupPositionAction) undoAction;
1153                                                isUpdate = true;
1154                                                int iNode = 0;
1155                                                while (isUpdate && iNode < posAction.m_nodes.size()) {
1156                                                        if ((Integer)posAction.m_nodes.elementAt(iNode) != (Integer) nodes.elementAt(iNode)) {
1157                                                                isUpdate = false;
1158                                                        }
1159                                                        iNode++;
1160                                                }
1161                                                if (isUpdate == true) {
1162                                                        posAction.setUndoPosition(dX, dY);
1163                                                }
1164                                }
1165                        } catch (Exception e) {
1166                                // ignore. it's not a SetPositionAction
1167                        }
1168                        if (!isUpdate) {
1169                                addUndoAction(new SetGroupPositionAction(nodes, dX, dY));
1170                        }
1171                }
1172                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1173                        nNode = (Integer) nodes.elementAt(iNode);
1174                        m_nPositionX.setElementAt(getPositionX(nNode) + dX, nNode);
1175                        m_nPositionY.setElementAt(getPositionY(nNode) + dY, nNode);
1176                }
1177        } // setPosition
1178
1179        /** set positions of all nodes
1180         * @param nPosX new x positions for all nodes
1181         * @param nPosY new y positions for all nodes
1182         */
1183        public void layoutGraph(FastVector nPosX, FastVector nPosY) {
1184                if (m_bNeedsUndoAction) {
1185                        addUndoAction(new LayoutGraphAction(nPosX, nPosY));
1186                }
1187                m_nPositionX = nPosX;
1188                m_nPositionY = nPosY;
1189        } // layoutGraph
1190
1191        /** get x position of a node
1192         * @param iNode index of node of interest
1193         */
1194        public int getPositionX(int iNode) {
1195                return (Integer) (m_nPositionX.elementAt(iNode));
1196        }
1197
1198        /** get y position of a node
1199         * @param iNode index of node of interest
1200         */
1201        public int getPositionY(int iNode) {
1202                return (Integer) (m_nPositionY.elementAt(iNode));
1203        }
1204
1205        /** align set of nodes with the left most node in the list
1206         * @param nodes list of indexes of nodes to align
1207         */
1208        public void alignLeft(FastVector nodes) {
1209                // update undo stack
1210                if (m_bNeedsUndoAction) {
1211                        addUndoAction(new alignLeftAction(nodes));
1212                }
1213                int nMinX = -1;
1214                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1215                        int nX = getPositionX((Integer) nodes.elementAt(iNode));
1216                        if (nX < nMinX || iNode == 0) {
1217                                nMinX = nX;
1218                        }
1219                }
1220                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1221                        int nNode = (Integer) nodes.elementAt(iNode);
1222                        m_nPositionX.setElementAt(nMinX, nNode);
1223                }
1224        } // alignLeft
1225
1226        /** align set of nodes with the right most node in the list
1227         * @param nodes list of indexes of nodes to align
1228         */
1229        public void alignRight(FastVector nodes) {
1230                // update undo stack
1231                if (m_bNeedsUndoAction) {
1232                        addUndoAction(new alignRightAction(nodes));
1233                }
1234                int nMaxX = -1;
1235                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1236                        int nX = getPositionX((Integer) nodes.elementAt(iNode));
1237                        if (nX > nMaxX || iNode == 0) {
1238                                nMaxX = nX;
1239                        }
1240                }
1241                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1242                        int nNode = (Integer) nodes.elementAt(iNode);
1243                        m_nPositionX.setElementAt(nMaxX, nNode);
1244                }
1245        } // alignRight
1246
1247        /** align set of nodes with the top most node in the list
1248         * @param nodes list of indexes of nodes to align
1249         */
1250        public void alignTop(FastVector nodes) {
1251                // update undo stack
1252                if (m_bNeedsUndoAction) {
1253                        addUndoAction(new alignTopAction(nodes));
1254                }
1255                int nMinY = -1;
1256                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1257                        int nY = getPositionY((Integer) nodes.elementAt(iNode));
1258                        if (nY < nMinY || iNode == 0) {
1259                                nMinY = nY;
1260                        }
1261                }
1262                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1263                        int nNode = (Integer) nodes.elementAt(iNode);
1264                        m_nPositionY.setElementAt(nMinY, nNode);
1265                }
1266        } // alignTop
1267
1268        /** align set of nodes with the bottom most node in the list
1269         * @param nodes list of indexes of nodes to align
1270         */
1271        public void alignBottom(FastVector nodes) {
1272                // update undo stack
1273                if (m_bNeedsUndoAction) {
1274                        addUndoAction(new alignBottomAction(nodes));
1275                }
1276                int nMaxY = -1;
1277                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1278                        int nY = getPositionY((Integer) nodes.elementAt(iNode));
1279                        if (nY > nMaxY || iNode == 0) {
1280                                nMaxY = nY;
1281                        }
1282                }
1283                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1284                        int nNode = (Integer) nodes.elementAt(iNode);
1285                        m_nPositionY.setElementAt(nMaxY, nNode);
1286                }
1287        } // alignBottom
1288
1289        /** center set of nodes half way between left and right most node in the list
1290         * @param nodes list of indexes of nodes to center
1291         */
1292        public void centerHorizontal(FastVector nodes) {
1293                // update undo stack
1294                if (m_bNeedsUndoAction) {
1295                        addUndoAction(new centerHorizontalAction(nodes));
1296                }
1297                int nMinY = -1;
1298                int nMaxY = -1;
1299                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1300                        int nY = getPositionY((Integer) nodes.elementAt(iNode));
1301                        if (nY < nMinY || iNode == 0) {
1302                                nMinY = nY;
1303                        }
1304                        if (nY > nMaxY || iNode == 0) {
1305                                nMaxY = nY;
1306                        }
1307                }
1308                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1309                        int nNode = (Integer) nodes.elementAt(iNode);
1310                        m_nPositionY.setElementAt((nMinY + nMaxY) / 2, nNode);
1311                }
1312        } // centerHorizontal
1313
1314        /** center set of nodes half way between top and bottom most node in the list
1315         * @param nodes list of indexes of nodes to center
1316         */
1317        public void centerVertical(FastVector nodes) {
1318                // update undo stack
1319                if (m_bNeedsUndoAction) {
1320                        addUndoAction(new centerVerticalAction(nodes));
1321                }
1322                int nMinX = -1;
1323                int nMaxX = -1;
1324                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1325                        int nX = getPositionX((Integer) nodes.elementAt(iNode));
1326                        if (nX < nMinX || iNode == 0) {
1327                                nMinX = nX;
1328                        }
1329                        if (nX > nMaxX || iNode == 0) {
1330                                nMaxX = nX;
1331                        }
1332                }
1333                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1334                        int nNode = (Integer) nodes.elementAt(iNode);
1335                        m_nPositionX.setElementAt((nMinX + nMaxX) / 2, nNode);
1336                }
1337        } // centerVertical
1338
1339        /** space out set of nodes evenly between left and right most node in the list
1340         * @param nodes list of indexes of nodes to space out
1341         */
1342        public void spaceHorizontal(FastVector nodes) {
1343                // update undo stack
1344                if (m_bNeedsUndoAction) {
1345                        addUndoAction(new spaceHorizontalAction(nodes));
1346                }
1347                int nMinX = -1;
1348                int nMaxX = -1;
1349                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1350                        int nX = getPositionX((Integer) nodes.elementAt(iNode));
1351                        if (nX < nMinX || iNode == 0) {
1352                                nMinX = nX;
1353                        }
1354                        if (nX > nMaxX || iNode == 0) {
1355                                nMaxX = nX;
1356                        }
1357                }
1358                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1359                        int nNode = (Integer) nodes.elementAt(iNode);
1360                        m_nPositionX.setElementAt((int) (nMinX + iNode * (nMaxX - nMinX) / (nodes.size() - 1.0)), nNode);
1361                }
1362        } // spaceHorizontal
1363
1364        /** space out set of nodes evenly between top and bottom most node in the list
1365         * @param nodes list of indexes of nodes to space out
1366         */
1367        public void spaceVertical(FastVector nodes) {
1368                // update undo stack
1369                if (m_bNeedsUndoAction) {
1370                        addUndoAction(new spaceVerticalAction(nodes));
1371                }
1372                int nMinY = -1;
1373                int nMaxY = -1;
1374                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1375                        int nY = getPositionY((Integer) nodes.elementAt(iNode));
1376                        if (nY < nMinY || iNode == 0) {
1377                                nMinY = nY;
1378                        }
1379                        if (nY > nMaxY || iNode == 0) {
1380                                nMaxY = nY;
1381                        }
1382                }
1383                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1384                        int nNode = (Integer) nodes.elementAt(iNode);
1385                        m_nPositionY.setElementAt((int) (nMinY + iNode * (nMaxY - nMinY) / (nodes.size() - 1.0)), nNode);
1386                }
1387        } // spaceVertical
1388
1389
1390        /** replace attribute with specified name and values
1391         * @param nTargetNode index of node the replace specification for
1392         * @param sName new name of the node
1393         * @param values array of values of the node
1394         */
1395        void replaceAtt(int nTargetNode, String sName, FastVector values) {
1396                Attribute newAtt = new Attribute(sName, values);
1397                if (m_Instances.classIndex() == nTargetNode) {
1398                        m_Instances.setClassIndex(-1);
1399                        m_Instances.insertAttributeAt(newAtt, nTargetNode);
1400                        m_Instances.deleteAttributeAt(nTargetNode + 1);
1401                        m_Instances.setClassIndex(nTargetNode);
1402                } else {
1403                        m_Instances.insertAttributeAt(newAtt, nTargetNode);
1404                        m_Instances.deleteAttributeAt(nTargetNode + 1);
1405                }
1406        } // replaceAtt
1407
1408        /** return marginal distibution for a node
1409         * @param iNode index of node of interest
1410         */
1411        public double[] getMargin(int iNode) {
1412                return (double[]) m_fMarginP.elementAt(iNode);
1413        };
1414
1415        /** set marginal distibution for a node
1416         * @param iNode index of node to set marginal distribution for
1417         * @param fMarginP marginal distribution
1418         */
1419        public void setMargin(int iNode, double[] fMarginP) {
1420                m_fMarginP.setElementAt(fMarginP, iNode);
1421        }
1422
1423        /** get evidence state of a node. -1 represents no evidence set, otherwise
1424         * the index of a value of the node
1425         * @param iNode index of node of interest
1426         */
1427        public int getEvidence(int iNode) {
1428                return (Integer) m_nEvidence.elementAt(iNode);
1429        }
1430
1431        /** set evidence state of a node. -1 represents no evidence set, otherwise
1432         * the index of a value of the node
1433         * @param iNode index of node of interest
1434         * @param iValue evidence value to set
1435         */
1436        public void setEvidence(int iNode, int iValue) {
1437                m_nEvidence.setElementAt(iValue, iNode);
1438        }
1439
1440        /** return list of children of a node
1441         * @param nTargetNode index of node of interest
1442         */
1443        public FastVector getChildren(int nTargetNode) {
1444                FastVector children = new FastVector();
1445                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
1446                        if (m_ParentSets[iNode].contains(nTargetNode)) {
1447                                children.addElement(iNode);
1448                        }
1449                }
1450                return children;
1451        } // getChildren
1452
1453        /** returns network in XMLBIF format
1454        */
1455        public String toXMLBIF03() {
1456                if (m_Instances == null) {
1457                        return ("<!--No model built yet-->");
1458                }
1459
1460                StringBuffer text = new StringBuffer();
1461                text.append(getBIFHeader());
1462                text.append("\n");
1463                text.append("\n");
1464                text.append("<BIF VERSION=\"0.3\">\n");
1465                text.append("<NETWORK>\n");
1466                text.append("<NAME>" + XMLNormalize(m_Instances.relationName()) + "</NAME>\n");
1467                for (int iAttribute = 0; iAttribute < m_Instances.numAttributes(); iAttribute++) {
1468                        text.append("<VARIABLE TYPE=\"nature\">\n");
1469                        text.append("<NAME>" + XMLNormalize(m_Instances.attribute(iAttribute).name()) + "</NAME>\n");
1470                        for (int iValue = 0; iValue < m_Instances.attribute(iAttribute).numValues(); iValue++) {
1471                                text.append("<OUTCOME>" + XMLNormalize(m_Instances.attribute(iAttribute).value(iValue))
1472                                                + "</OUTCOME>\n");
1473                        }
1474                        text.append("<PROPERTY>position = (" + getPositionX(iAttribute) + "," + getPositionY(iAttribute)
1475                                        + ")</PROPERTY>\n");
1476                        text.append("</VARIABLE>\n");
1477                }
1478
1479                for (int iAttribute = 0; iAttribute < m_Instances.numAttributes(); iAttribute++) {
1480                        text.append("<DEFINITION>\n");
1481                        text.append("<FOR>" + XMLNormalize(m_Instances.attribute(iAttribute).name()) + "</FOR>\n");
1482                        for (int iParent = 0; iParent < m_ParentSets[iAttribute].getNrOfParents(); iParent++) {
1483                                text.append("<GIVEN>"
1484                                                + XMLNormalize(m_Instances.attribute(m_ParentSets[iAttribute].getParent(iParent)).name())
1485                                                + "</GIVEN>\n");
1486                        }
1487                        text.append("<TABLE>\n");
1488                        for (int iParent = 0; iParent < m_ParentSets[iAttribute].getCardinalityOfParents(); iParent++) {
1489                                for (int iValue = 0; iValue < m_Instances.attribute(iAttribute).numValues(); iValue++) {
1490                                        text.append(m_Distributions[iAttribute][iParent].getProbability(iValue));
1491                                        text.append(' ');
1492                                }
1493                                text.append('\n');
1494                        }
1495                        text.append("</TABLE>\n");
1496                        text.append("</DEFINITION>\n");
1497                }
1498                text.append("</NETWORK>\n");
1499                text.append("</BIF>\n");
1500                return text.toString();
1501        } // toXMLBIF03
1502
1503        /** return fragment of network in XMLBIF format
1504         * @param nodes array of indexes of nodes that should be in the fragment
1505         */
1506        public String toXMLBIF03(FastVector nodes) {
1507                StringBuffer text = new StringBuffer();
1508                text.append(getBIFHeader());
1509                text.append("\n");
1510                text.append("\n");
1511                text.append("<BIF VERSION=\"0.3\">\n");
1512                text.append("<NETWORK>\n");
1513                text.append("<NAME>" + XMLNormalize(m_Instances.relationName()) + "</NAME>\n");
1514                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1515                        int nNode = (Integer) nodes.elementAt(iNode);
1516                        text.append("<VARIABLE TYPE=\"nature\">\n");
1517                        text.append("<NAME>" + XMLNormalize(m_Instances.attribute(nNode).name()) + "</NAME>\n");
1518                        for (int iValue = 0; iValue < m_Instances.attribute(nNode).numValues(); iValue++) {
1519                                text.append("<OUTCOME>" + XMLNormalize(m_Instances.attribute(nNode).value(iValue)) + "</OUTCOME>\n");
1520                        }
1521                        text.append("<PROPERTY>position = (" + getPositionX(nNode) + "," + getPositionY(nNode) + ")</PROPERTY>\n");
1522                        text.append("</VARIABLE>\n");
1523                }
1524
1525                for (int iNode = 0; iNode < nodes.size(); iNode++) {
1526                        int nNode = (Integer) nodes.elementAt(iNode);
1527                        text.append("<DEFINITION>\n");
1528                        text.append("<FOR>" + XMLNormalize(m_Instances.attribute(nNode).name()) + "</FOR>\n");
1529                        for (int iParent = 0; iParent < m_ParentSets[nNode].getNrOfParents(); iParent++) {
1530                                text.append("<GIVEN>"
1531                                                + XMLNormalize(m_Instances.attribute(m_ParentSets[nNode].getParent(iParent)).name())
1532                                                + "</GIVEN>\n");
1533                        }
1534                        text.append("<TABLE>\n");
1535                        for (int iParent = 0; iParent < m_ParentSets[nNode].getCardinalityOfParents(); iParent++) {
1536                                for (int iValue = 0; iValue < m_Instances.attribute(nNode).numValues(); iValue++) {
1537                                        text.append(m_Distributions[nNode][iParent].getProbability(iValue));
1538                                        text.append(' ');
1539                                }
1540                                text.append('\n');
1541                        }
1542                        text.append("</TABLE>\n");
1543                        text.append("</DEFINITION>\n");
1544                }
1545                text.append("</NETWORK>\n");
1546                text.append("</BIF>\n");
1547                return text.toString();
1548        } // toXMLBIF03
1549
1550        /** undo stack for undoin edit actions, or redo edit actions */
1551        FastVector m_undoStack = new FastVector();
1552
1553        /** current action in undo stack */
1554        int m_nCurrentEditAction = -1;
1555
1556        /** action that the network is saved */
1557        int m_nSavedPointer = -1;
1558
1559        /***************************************************************************
1560         * flag to indicate whether an edit action needs to introduce an undo
1561         * action. This is only false when an undo or redo action is performed.
1562         **************************************************************************/
1563        boolean m_bNeedsUndoAction = true;
1564
1565        /** return whether there is something on the undo stack that can be performed */
1566        public boolean canUndo() {
1567                return m_nCurrentEditAction >= 0;
1568        }
1569
1570        /** return whether there is something on the undo stack that can be performed */
1571        public boolean canRedo() {
1572                return m_nCurrentEditAction < m_undoStack.size() - 1;
1573        }
1574
1575        /** return true when current state differs from the state the network was last saved */
1576        public boolean isChanged() {
1577                return m_nCurrentEditAction != m_nSavedPointer;
1578        }
1579
1580        /** indicate the network state was saved */
1581        public void isSaved() {
1582                m_nSavedPointer = m_nCurrentEditAction;
1583        }
1584
1585        /** get message representing the last action performed on the network */
1586        public String lastActionMsg() {
1587                if (m_undoStack.size() == 0) {
1588                        return "";
1589                }
1590                return ((UndoAction) m_undoStack.lastElement()).getRedoMsg();
1591        } // lastActionMsg
1592
1593
1594        /** undo the last edit action performed on the network.
1595         * returns message representing the action performed.
1596         */
1597        public String undo() {
1598                if (!canUndo()) {
1599                        return "";
1600                }
1601                UndoAction undoAction = (UndoAction) m_undoStack.elementAt(m_nCurrentEditAction);
1602                m_bNeedsUndoAction = false;
1603                undoAction.undo();
1604                m_bNeedsUndoAction = true;
1605                m_nCurrentEditAction--;
1606
1607                // undo stack debugging
1608                /*
1609                if (m_nCurrentEditAction>0) {
1610                        String sXML = (String) m_sXMLStack.elementAt(m_nCurrentEditAction);
1611                        String sXMLCurrent = toXMLBIF03();
1612                        if (!sXML.equals(sXMLCurrent)) {
1613                                String sDiff = "";
1614                                String sDiff2 = "";
1615                                for (int i = 0; i < sXML.length() && sDiff.length() < 80; i++) {
1616                                        if (sXML.charAt(i) != sXMLCurrent.charAt(i)) {
1617                                                sDiff += sXML.charAt(i);
1618                                                sDiff2 += sXMLCurrent.charAt(i);
1619                                        }
1620                                }
1621
1622                                JOptionPane.showMessageDialog(null,"Undo error\n" + sDiff + " \n" + sDiff2);
1623                        }
1624                }
1625                */
1626                return undoAction.getUndoMsg();
1627        } // undo
1628
1629        /** redo the last edit action performed on the network.
1630         * returns message representing the action performed.
1631         */
1632        public String redo() {
1633                if (!canRedo()) {
1634                        return "";
1635                }
1636                m_nCurrentEditAction++;
1637                UndoAction undoAction = (UndoAction) m_undoStack.elementAt(m_nCurrentEditAction);
1638                m_bNeedsUndoAction = false;
1639                undoAction.redo();
1640                m_bNeedsUndoAction = true;
1641
1642                // undo stack debugging
1643                /*
1644                if (m_nCurrentEditAction < m_sXMLStack.size()) {
1645                        String sXML = (String) m_sXMLStack.elementAt(m_nCurrentEditAction);
1646                        String sXMLCurrent = toXMLBIF03();
1647                        if (!sXML.equals(sXMLCurrent)) {
1648                                String sDiff = "";
1649                                String sDiff2 = "";
1650                                for (int i = 0; i < sXML.length() && sDiff.length() < 80; i++) {
1651                                        if (sXML.charAt(i) != sXMLCurrent.charAt(i)) {
1652                                                sDiff += sXML.charAt(i);
1653                                                sDiff2 += sXMLCurrent.charAt(i);
1654                                        }
1655                                }
1656
1657                                JOptionPane.showMessageDialog(null,"redo error\n" + sDiff + " \n" + sDiff2);
1658                        }
1659                }
1660                */
1661                return undoAction.getRedoMsg();
1662        } // redo
1663
1664        /** add undo action to the undo stack.
1665         * @param action operation that needs to be added to the undo stack
1666         */
1667        void addUndoAction(UndoAction action) {
1668                int iAction = m_undoStack.size() - 1;
1669                while (iAction > m_nCurrentEditAction) {
1670                        m_undoStack.removeElementAt(iAction--);
1671                }
1672                if (m_nSavedPointer > m_nCurrentEditAction) {
1673                        m_nSavedPointer = -2;
1674                }
1675                m_undoStack.addElement(action);
1676                //m_sXMLStack.addElement(toXMLBIF03());
1677                m_nCurrentEditAction++;
1678        } // addUndoAction
1679
1680        /** remove all actions from the undo stack */
1681        public void clearUndoStack() {
1682                m_undoStack = new FastVector();
1683                //m_sXMLStack = new FastVector();
1684                m_nCurrentEditAction = -1;
1685                m_nSavedPointer = -1;
1686        } // clearUndoStack
1687
1688        /** base class for actions representing operations on the Bayesian network
1689         * that can be undone/redone
1690         */
1691        class UndoAction implements Serializable {
1692                /** for serialization */
1693                static final long serialVersionUID = 1;
1694                public void undo() {
1695                }
1696
1697                public void redo() {
1698                }
1699
1700                public String getUndoMsg() {
1701                        return getMsg();
1702                }
1703
1704                public String getRedoMsg() {
1705                        return getMsg();
1706                }
1707                String getMsg() {
1708                        String sStr = toString();
1709                        int iStart = sStr.indexOf('$');
1710                        int iEnd = sStr.indexOf('@');
1711                        StringBuffer sBuffer = new StringBuffer();
1712                        for(int i= iStart + 1; i < iEnd; i++) {
1713                                char c = sStr.charAt(i);
1714                                if (Character.isUpperCase(c)) {
1715                                        sBuffer.append(' ');
1716                                }
1717                                sBuffer.append(sStr.charAt(i));
1718                        }
1719                        return sBuffer.toString();
1720                } // getMsg
1721        } // class UndoAction
1722
1723        class AddNodeAction extends UndoAction {
1724                /** for serialization */
1725                static final long serialVersionUID = 1;
1726                String m_sName;
1727                int m_nPosX;
1728                int m_nPosY;
1729
1730                int m_nCardinality;
1731
1732                AddNodeAction(String sName, int nCardinality, int nPosX, int nPosY) {
1733                        m_sName = sName;
1734                        m_nCardinality = nCardinality;
1735                        m_nPosX = nPosX;
1736                        m_nPosY = nPosY;
1737                } // c'tor
1738
1739                public void undo() {
1740                        try {
1741                                deleteNode(getNrOfNodes() - 1);
1742                        } catch (Exception e) {
1743                                e.printStackTrace();
1744                        }
1745                } // undo
1746
1747                public void redo() {
1748                        try {
1749                                addNode(m_sName, m_nCardinality, m_nPosX, m_nPosY);
1750                        } catch (Exception e) {
1751                                e.printStackTrace();
1752                        }
1753                } // redo
1754        } // class AddNodeAction
1755
1756        class DeleteNodeAction extends UndoAction {
1757                /** for serialization */
1758                static final long serialVersionUID = 1;
1759                int m_nTargetNode;
1760
1761                Attribute m_att;
1762
1763                Estimator[] m_CPT;
1764
1765                ParentSet m_ParentSet;
1766
1767                FastVector m_deleteArcActions;
1768
1769                int m_nPosX;
1770
1771                int m_nPosY;
1772
1773                DeleteNodeAction(int nTargetNode) {
1774                        m_nTargetNode = nTargetNode;
1775                        m_att = m_Instances.attribute(nTargetNode);
1776                        try {
1777                                SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
1778                                m_CPT = (Estimator[]) so.getObject();
1779                                ;
1780                                so = new SerializedObject(m_ParentSets[nTargetNode]);
1781                                m_ParentSet = (ParentSet) so.getObject();
1782                        } catch (Exception e) {
1783                                e.printStackTrace();
1784                        }
1785                        m_deleteArcActions = new FastVector();
1786                        for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
1787                                if (m_ParentSets[iNode].contains(nTargetNode)) {
1788                                        m_deleteArcActions.addElement(new DeleteArcAction(nTargetNode, iNode));
1789                                }
1790                        }
1791                        m_nPosX = getPositionX(m_nTargetNode);
1792                        m_nPosY = getPositionY(m_nTargetNode);
1793                } // c'tor
1794
1795                public void undo() {
1796                        try {
1797                                m_Instances.insertAttributeAt(m_att, m_nTargetNode);
1798                                int nAtts = m_Instances.numAttributes();
1799                                // update parentsets
1800                                ParentSet[] parentSets = new ParentSet[nAtts];
1801                                int nX = 0;
1802                                for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
1803                                        if (iParentSet == m_nTargetNode) {
1804                                                SerializedObject so = new SerializedObject(m_ParentSet);
1805                                                parentSets[iParentSet] = (ParentSet) so.getObject();
1806                                                nX = 1;
1807                                        } else {
1808                                                parentSets[iParentSet] = m_ParentSets[iParentSet - nX];
1809                                                for (int iParent = 0; iParent < parentSets[iParentSet].getNrOfParents(); iParent++) {
1810                                                        int nParent = parentSets[iParentSet].getParent(iParent);
1811                                                        if (nParent >= m_nTargetNode) {
1812                                                                parentSets[iParentSet].SetParent(iParent, nParent + 1);
1813                                                        }
1814                                                }
1815                                        }
1816                                }
1817                                m_ParentSets = parentSets;
1818                                // update distributions
1819                                Estimator[][] distributions = new Estimator[nAtts][];
1820                                nX = 0;
1821                                for (int iNode = 0; iNode < nAtts; iNode++) {
1822                                        if (iNode == m_nTargetNode) {
1823                                                SerializedObject so = new SerializedObject(m_CPT);
1824                                                distributions[iNode] = (Estimator[]) so.getObject();
1825                                                nX = 1;
1826                                        } else {
1827                                                distributions[iNode] = m_Distributions[iNode - nX];
1828                                        }
1829                                }
1830                                m_Distributions = distributions;
1831
1832                                for (int deletedArc = 0; deletedArc < m_deleteArcActions.size(); deletedArc++) {
1833                                        DeleteArcAction action = (DeleteArcAction) m_deleteArcActions.elementAt(deletedArc);
1834                                        action.undo();
1835                                }
1836                                m_nPositionX.insertElementAt(m_nPosX, m_nTargetNode);
1837                                m_nPositionY.insertElementAt(m_nPosY, m_nTargetNode);
1838                                m_nEvidence.insertElementAt(-1, m_nTargetNode);
1839                                m_fMarginP.insertElementAt(new double[getCardinality(m_nTargetNode)], m_nTargetNode);
1840                        } catch (Exception e) {
1841                                e.printStackTrace();
1842                        }
1843                } // undo
1844
1845                public void redo() {
1846                        try {
1847                                deleteNode(m_nTargetNode);
1848                        } catch (Exception e) {
1849                                e.printStackTrace();
1850                        }
1851                } // redo
1852        } // class DeleteNodeAction
1853
1854        class DeleteSelectionAction extends UndoAction {
1855                /** for serialization */
1856                static final long serialVersionUID = 1;
1857                FastVector m_nodes;
1858
1859                Attribute[] m_att;
1860
1861                Estimator[][] m_CPT;
1862
1863                ParentSet[] m_ParentSet;
1864
1865                FastVector m_deleteArcActions;
1866
1867                int[] m_nPosX;
1868
1869                int[] m_nPosY;
1870
1871                public DeleteSelectionAction(FastVector nodes) {
1872                        m_nodes = new FastVector();
1873                        int nNodes = nodes.size();
1874                        m_att = new Attribute[nNodes];
1875                        m_CPT = new Estimator[nNodes][];
1876                        m_ParentSet = new ParentSet[nNodes];
1877                        m_nPosX = new int[nNodes];
1878                        m_nPosY = new int[nNodes];
1879                        m_deleteArcActions = new FastVector();
1880                        for (int iNode = 0; iNode < nodes.size(); iNode++) {
1881                                int nTargetNode = (Integer) nodes.elementAt(iNode);
1882                                m_nodes.addElement(nTargetNode);
1883                                m_att[iNode] = m_Instances.attribute(nTargetNode);
1884                                try {
1885                                        SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
1886                                        m_CPT[iNode] = (Estimator[]) so.getObject();
1887                                        ;
1888                                        so = new SerializedObject(m_ParentSets[nTargetNode]);
1889                                        m_ParentSet[iNode] = (ParentSet) so.getObject();
1890                                } catch (Exception e) {
1891                                        e.printStackTrace();
1892                                }
1893                                m_nPosX[iNode] = getPositionX(nTargetNode);
1894                                m_nPosY[iNode] = getPositionY(nTargetNode);
1895                                for (int iNode2 = 0; iNode2 < getNrOfNodes(); iNode2++) {
1896                                        if (!nodes.contains(iNode2) && m_ParentSets[iNode2].contains(nTargetNode)) {
1897                                                m_deleteArcActions.addElement(new DeleteArcAction(nTargetNode, iNode2));
1898                                        }
1899                                }
1900                        }
1901                } // c'tor
1902
1903                public void undo() {
1904                        try {
1905                                for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
1906                                        int nTargetNode = (Integer) m_nodes.elementAt(iNode);
1907                                        m_Instances.insertAttributeAt(m_att[iNode], nTargetNode);
1908                                }
1909                                int nAtts = m_Instances.numAttributes();
1910                                // update parentsets
1911                                ParentSet[] parentSets = new ParentSet[nAtts];
1912                                int[] offset = new int[nAtts];
1913                                for (int iNode = 0; iNode < nAtts; iNode++) {
1914                                        offset[iNode] = iNode;
1915                                }
1916                                for (int iNode = m_nodes.size() - 1; iNode >= 0; iNode--) {
1917                                        int nTargetNode = (Integer) m_nodes.elementAt(iNode);
1918                                        for (int i = nTargetNode; i < nAtts - 1; i++) {
1919                                                offset[i] = offset[i + 1];
1920                                        }
1921                                }
1922
1923                                int iTargetNode = 0;
1924                                for (int iParentSet = 0; iParentSet < nAtts; iParentSet++) {
1925                                        if (iTargetNode < m_nodes.size()
1926                                                        && (Integer) m_nodes.elementAt(iTargetNode) == (Integer) iParentSet) {
1927                                                SerializedObject so = new SerializedObject(m_ParentSet[iTargetNode]);
1928                                                parentSets[iParentSet] = (ParentSet) so.getObject();
1929                                                iTargetNode++;
1930                                        } else {
1931                                                parentSets[iParentSet] = m_ParentSets[iParentSet - iTargetNode];
1932                                                for (int iParent = 0; iParent < parentSets[iParentSet].getNrOfParents(); iParent++) {
1933                                                        int nParent = parentSets[iParentSet].getParent(iParent);
1934                                                        parentSets[iParentSet].SetParent(iParent, offset[nParent]);
1935                                                }
1936                                        }
1937                                }
1938                                m_ParentSets = parentSets;
1939                                // update distributions
1940                                Estimator[][] distributions = new Estimator[nAtts][];
1941                                iTargetNode = 0;
1942                                for (int iNode = 0; iNode < nAtts; iNode++) {
1943                                        if (iTargetNode < m_nodes.size() && (Integer) m_nodes.elementAt(iTargetNode) == (Integer) iNode) {
1944                                                SerializedObject so = new SerializedObject(m_CPT[iTargetNode]);
1945                                                distributions[iNode] = (Estimator[]) so.getObject();
1946                                                iTargetNode++;
1947                                        } else {
1948                                                distributions[iNode] = m_Distributions[iNode - iTargetNode];
1949                                        }
1950                                }
1951                                m_Distributions = distributions;
1952
1953                                for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
1954                                        int nTargetNode = (Integer) m_nodes.elementAt(iNode);
1955                                        m_nPositionX.insertElementAt(m_nPosX[iNode], nTargetNode);
1956                                        m_nPositionY.insertElementAt(m_nPosY[iNode], nTargetNode);
1957                                        m_nEvidence.insertElementAt(-1, nTargetNode);
1958                                        m_fMarginP.insertElementAt(new double[getCardinality(nTargetNode)], nTargetNode);
1959                                }
1960                                for (int deletedArc = 0; deletedArc < m_deleteArcActions.size(); deletedArc++) {
1961                                        DeleteArcAction action = (DeleteArcAction) m_deleteArcActions.elementAt(deletedArc);
1962                                        action.undo();
1963                                }
1964                        } catch (Exception e) {
1965                                e.printStackTrace();
1966                        }
1967                } // undo
1968
1969                public void redo() {
1970                        try {
1971                                for (int iNode = m_nodes.size() - 1; iNode >= 0; iNode--) {
1972                                        int nNode = (Integer) m_nodes.elementAt(iNode);
1973                                        deleteNode(nNode);
1974                                }
1975                        } catch (Exception e) {
1976                                e.printStackTrace();
1977                        }
1978                } // redo
1979        } // class DeleteSelectionAction
1980
1981        class AddArcAction extends UndoAction {
1982                /** for serialization */
1983                static final long serialVersionUID = 1;
1984                //int m_nChild;
1985                FastVector m_children;
1986
1987                int m_nParent;
1988
1989                Estimator[][] m_CPT;
1990
1991                AddArcAction(int nParent, int nChild) {
1992                        try {
1993                                m_nParent = nParent;
1994                                m_children = new FastVector();
1995                                m_children.addElement(nChild);
1996                                //m_nChild = nChild;
1997                                SerializedObject so = new SerializedObject(m_Distributions[nChild]);
1998                                m_CPT = new Estimator[1][];
1999                                m_CPT[0] = (Estimator[]) so.getObject();
2000                                ;
2001                        } catch (Exception e) {
2002                                e.printStackTrace();
2003                        }
2004                } // c'tor
2005
2006                AddArcAction(int nParent, FastVector children) {
2007                        try {
2008                                m_nParent = nParent;
2009                                m_children = new FastVector();
2010                                m_CPT = new Estimator[children.size()][];
2011                                for (int iChild = 0; iChild < children.size(); iChild++) {
2012                                        int nChild = (Integer) children.elementAt(iChild);
2013                                        m_children.addElement(nChild);
2014                                        SerializedObject so = new SerializedObject(m_Distributions[nChild]);
2015                                        m_CPT[iChild] = (Estimator[]) so.getObject();
2016                                }
2017                        } catch (Exception e) {
2018                                e.printStackTrace();
2019                        }
2020                } // c'tor
2021
2022                public void undo() {
2023                        try {
2024                                for (int iChild = 0; iChild < m_children.size(); iChild++) {
2025                                        int nChild = (Integer) m_children.elementAt(iChild);
2026                                        deleteArc(m_nParent, nChild);
2027                                        SerializedObject so = new SerializedObject(m_CPT[iChild]);
2028                                        m_Distributions[nChild] = (Estimator[]) so.getObject();
2029                                }
2030                        } catch (Exception e) {
2031                                e.printStackTrace();
2032                        }
2033                } // undo
2034
2035                public void redo() {
2036                        try {
2037                                for (int iChild = 0; iChild < m_children.size(); iChild++) {
2038                                        int nChild = (Integer) m_children.elementAt(iChild);
2039                                        addArc(m_nParent, nChild);
2040                                }
2041                        } catch (Exception e) {
2042                                e.printStackTrace();
2043                        }
2044                } // redo
2045        } // class AddArcAction
2046
2047        class DeleteArcAction extends UndoAction {
2048                /** for serialization */
2049                static final long serialVersionUID = 1;
2050                int[] m_nParents;
2051                int m_nChild;
2052                int m_nParent;
2053                Estimator[] m_CPT;
2054
2055                DeleteArcAction(int nParent, int nChild) {
2056                        try {
2057                        m_nChild = nChild;
2058                        m_nParent = nParent;
2059                        m_nParents = new int[getNrOfParents(nChild)];
2060                        for (int iParent = 0; iParent < m_nParents.length; iParent++) {
2061                                m_nParents[iParent] = getParent(nChild, iParent);
2062                        }
2063                        SerializedObject so = new SerializedObject(m_Distributions[nChild]);
2064                        m_CPT = (Estimator[]) so.getObject();
2065                        } catch (Exception e) {
2066                                e.printStackTrace();
2067                        }
2068                } // c'tor
2069
2070                public void undo() {
2071                        try {
2072                                SerializedObject so = new SerializedObject(m_CPT);
2073                                m_Distributions[m_nChild] = (Estimator[]) so.getObject();
2074                                ParentSet parentSet = new ParentSet();
2075                                for (int iParent = 0; iParent < m_nParents.length; iParent++) {
2076                                        parentSet.addParent(m_nParents[iParent], m_Instances);
2077                                }
2078                                m_ParentSets[m_nChild] = parentSet;
2079                        } catch (Exception e) {
2080                                e.printStackTrace();
2081                        }
2082                } // undo
2083
2084                public void redo() {
2085                        try {
2086                                deleteArc(m_nParent, m_nChild);
2087                        } catch (Exception e) {
2088                                e.printStackTrace();
2089                        }
2090                } // redo
2091        } // class DeleteArcAction
2092
2093        class SetDistributionAction extends UndoAction {
2094                /** for serialization */
2095                static final long serialVersionUID = 1;
2096                int m_nTargetNode;
2097
2098                Estimator[] m_CPT;
2099
2100                double[][] m_P;
2101
2102                SetDistributionAction(int nTargetNode, double[][] P) {
2103                        try {
2104                                m_nTargetNode = nTargetNode;
2105                                SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
2106                                m_CPT = (Estimator[]) so.getObject();
2107                                ;
2108                                m_P = P;
2109                        } catch (Exception e) {
2110                                e.printStackTrace();
2111                        }
2112                } // c'tor
2113
2114                public void undo() {
2115                        try {
2116                                SerializedObject so = new SerializedObject(m_CPT);
2117                                m_Distributions[m_nTargetNode] = (Estimator[]) so.getObject();
2118                        } catch (Exception e) {
2119                                e.printStackTrace();
2120                        }
2121                } // undo
2122
2123                public void redo() {
2124                        try {
2125                                setDistribution(m_nTargetNode, m_P);
2126                        } catch (Exception e) {
2127                                e.printStackTrace();
2128                        }
2129                } // redo
2130
2131                public String getUndoMsg() {
2132                        return "Distribution of node " + getNodeName(m_nTargetNode) + " changed";
2133                }
2134
2135                public String getRedoMsg() {
2136                        return "Distribution of node " + getNodeName(m_nTargetNode) + " changed";
2137                }
2138        } // class SetDistributionAction
2139
2140        class RenameAction extends UndoAction {
2141                /** for serialization */
2142                static final long serialVersionUID = 1;
2143                int m_nTargetNode;
2144
2145                String m_sNewName;
2146
2147                String m_sOldName;
2148
2149                RenameAction(int nTargetNode, String sOldName, String sNewName) {
2150                        m_nTargetNode = nTargetNode;
2151                        m_sNewName = sNewName;
2152                        m_sOldName = sOldName;
2153                } // c'tor
2154
2155                public void undo() {
2156                        setNodeName(m_nTargetNode, m_sOldName);
2157                } // undo
2158
2159                public void redo() {
2160                        setNodeName(m_nTargetNode, m_sNewName);
2161                } // redo
2162        } // class RenameAction
2163
2164        class RenameValueAction extends RenameAction {
2165                /** for serialization */
2166                static final long serialVersionUID = 1;
2167                RenameValueAction(int nTargetNode, String sOldName, String sNewName) {
2168                        super(nTargetNode, sOldName, sNewName);
2169                } // c'tor
2170
2171                public void undo() {
2172                        renameNodeValue(m_nTargetNode, m_sNewName, m_sOldName);
2173                } // undo
2174
2175                public void redo() {
2176                        renameNodeValue(m_nTargetNode, m_sOldName, m_sNewName);
2177                } // redo
2178
2179                public String getUndoMsg() {
2180                        return "Value of node " + getNodeName(m_nTargetNode) + " changed from " + m_sNewName + " to " + m_sOldName;
2181                }
2182
2183                public String getRedoMsg() {
2184                        return "Value of node " + getNodeName(m_nTargetNode) + " changed from " + m_sOldName + " to " + m_sNewName;
2185                }
2186        } // class RenameValueAction
2187
2188        class AddValueAction extends UndoAction {
2189                /** for serialization */
2190                static final long serialVersionUID = 1;
2191                int m_nTargetNode;
2192
2193                String m_sValue;
2194
2195                AddValueAction(int nTargetNode, String sValue) {
2196                        m_nTargetNode = nTargetNode;
2197                        m_sValue = sValue;
2198                } // c'tor
2199
2200                public void undo() {
2201                        try {
2202                                delNodeValue(m_nTargetNode, m_sValue);
2203                        } catch (Exception e) {
2204                                e.printStackTrace();
2205                        }
2206                } // undo
2207
2208                public void redo() {
2209                        addNodeValue(m_nTargetNode, m_sValue);
2210                } // redo
2211
2212                public String getUndoMsg() {
2213                        return "Value " + m_sValue + " removed from node " + getNodeName(m_nTargetNode);
2214                }
2215
2216                public String getRedoMsg() {
2217                        return "Value " + m_sValue + " added to node " + getNodeName(m_nTargetNode);
2218                }
2219        } // class AddValueAction
2220
2221        class DelValueAction extends UndoAction {
2222                /** for serialization */
2223                static final long serialVersionUID = 1;
2224                int m_nTargetNode;
2225
2226                String m_sValue;
2227
2228                Estimator[] m_CPT;
2229
2230                FastVector m_children;
2231
2232                Estimator[][] m_childAtts;
2233
2234                Attribute m_att;
2235
2236                DelValueAction(int nTargetNode, String sValue) {
2237                        try {
2238                                m_nTargetNode = nTargetNode;
2239                                m_sValue = sValue;
2240                                m_att = m_Instances.attribute(nTargetNode);
2241                                SerializedObject so = new SerializedObject(m_Distributions[nTargetNode]);
2242                                m_CPT = (Estimator[]) so.getObject();
2243                                ;
2244                                m_children = new FastVector();
2245                                for (int iNode = 0; iNode < getNrOfNodes(); iNode++) {
2246                                        if (m_ParentSets[iNode].contains(nTargetNode)) {
2247                                                m_children.addElement(iNode);
2248                                        }
2249                                }
2250                                m_childAtts = new Estimator[m_children.size()][];
2251                                for (int iChild = 0; iChild < m_children.size(); iChild++) {
2252                                        int nChild = (Integer) m_children.elementAt(iChild);
2253                                        m_childAtts[iChild] = m_Distributions[nChild];
2254                                }
2255                        } catch (Exception e) {
2256                                e.printStackTrace();
2257                        }
2258                } // c'tor
2259
2260                public void undo() {
2261                        try {
2262                                m_Instances.insertAttributeAt(m_att, m_nTargetNode);
2263                                SerializedObject so = new SerializedObject(m_CPT);
2264                                m_Distributions[m_nTargetNode] = (Estimator[]) so.getObject();
2265                                for (int iChild = 0; iChild < m_children.size(); iChild++) {
2266                                        int nChild = (Integer) m_children.elementAt(iChild);
2267                                        m_Instances.insertAttributeAt(m_att, m_nTargetNode);
2268                                        so = new SerializedObject(m_childAtts[iChild]);
2269                                        m_Distributions[nChild] = (Estimator[]) so.getObject();
2270                                }
2271                        } catch (Exception e) {
2272                                e.printStackTrace();
2273                        }
2274                } // undo
2275
2276                public void redo() {
2277                        try {
2278                                delNodeValue(m_nTargetNode, m_sValue);
2279                        } catch (Exception e) {
2280                                e.printStackTrace();
2281                        }
2282                } // redo
2283
2284                public String getUndoMsg() {
2285                        return "Value " + m_sValue + " added to node " + getNodeName(m_nTargetNode);
2286                }
2287
2288                public String getRedoMsg() {
2289                        return "Value " + m_sValue + " removed from node " + getNodeName(m_nTargetNode);
2290                }
2291        } // class DelValueAction
2292
2293        class alignAction extends UndoAction {
2294                /** for serialization */
2295                static final long serialVersionUID = 1;
2296                FastVector m_nodes;
2297
2298                FastVector m_posX;
2299
2300                FastVector m_posY;
2301
2302                alignAction(FastVector nodes) {
2303                        m_nodes = new FastVector(nodes.size());
2304                        m_posX = new FastVector(nodes.size());
2305                        m_posY = new FastVector(nodes.size());
2306                        for (int iNode = 0; iNode < nodes.size(); iNode++) {
2307                                int nNode = (Integer) nodes.elementAt(iNode);
2308                                m_nodes.addElement(nNode);
2309                                m_posX.addElement(getPositionX(nNode));
2310                                m_posY.addElement(getPositionY(nNode));
2311                        }
2312                } // c'tor
2313
2314                public void undo() {
2315                        try {
2316                                for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
2317                                        int nNode = (Integer) m_nodes.elementAt(iNode);
2318                                        setPosition(nNode, (Integer) m_posX.elementAt(iNode), (Integer) m_posY.elementAt(iNode));
2319                                }
2320                        } catch (Exception e) {
2321                                e.printStackTrace();
2322                        }
2323                } // undo
2324        } // class alignAction
2325
2326        class alignLeftAction extends alignAction {
2327                /** for serialization */
2328                static final long serialVersionUID = 1;
2329                public alignLeftAction(FastVector nodes) {
2330                        super(nodes);
2331                } // c'tor
2332
2333                public void redo() {
2334                        try {
2335                                alignLeft(m_nodes);
2336                        } catch (Exception e) {
2337                                e.printStackTrace();
2338                        }
2339                } // redo
2340
2341                public String getUndoMsg() {
2342                        return "Returning " + m_nodes.size() + " from aliging nodes to the left.";
2343                }
2344
2345                public String getRedoMsg() {
2346                        return "Aligning " + m_nodes.size() + " nodes to the left.";
2347                }
2348        } // class alignLeftAction
2349
2350        class alignRightAction extends alignAction {
2351                /** for serialization */
2352                static final long serialVersionUID = 1;
2353                public alignRightAction(FastVector nodes) {
2354                        super(nodes);
2355                } // c'tor
2356
2357                public void redo() {
2358                        try {
2359                                alignRight(m_nodes);
2360                        } catch (Exception e) {
2361                                e.printStackTrace();
2362                        }
2363                } // redo
2364
2365                public String getUndoMsg() {
2366                        return "Returning " + m_nodes.size() + " from aliging nodes to the right.";
2367                }
2368
2369                public String getRedoMsg() {
2370                        return "Aligning " + m_nodes.size() + " nodes to the right.";
2371                }
2372        } // class alignLeftAction
2373
2374        class alignTopAction extends alignAction {
2375                /** for serialization */
2376                static final long serialVersionUID = 1;
2377                public alignTopAction(FastVector nodes) {
2378                        super(nodes);
2379                } // c'tor
2380
2381                public void redo() {
2382                        try {
2383                                alignTop(m_nodes);
2384                        } catch (Exception e) {
2385                                e.printStackTrace();
2386                        }
2387                } // redo
2388
2389                public String getUndoMsg() {
2390                        return "Returning " + m_nodes.size() + " from aliging nodes to the top.";
2391                }
2392
2393                public String getRedoMsg() {
2394                        return "Aligning " + m_nodes.size() + " nodes to the top.";
2395                }
2396        } // class alignTopAction
2397
2398        class alignBottomAction extends alignAction {
2399                /** for serialization */
2400                static final long serialVersionUID = 1;
2401                public alignBottomAction(FastVector nodes) {
2402                        super(nodes);
2403                } // c'tor
2404
2405                public void redo() {
2406                        try {
2407                                alignBottom(m_nodes);
2408                        } catch (Exception e) {
2409                                e.printStackTrace();
2410                        }
2411                } // redo
2412
2413                public String getUndoMsg() {
2414                        return "Returning " + m_nodes.size() + " from aliging nodes to the bottom.";
2415                }
2416
2417                public String getRedoMsg() {
2418                        return "Aligning " + m_nodes.size() + " nodes to the bottom.";
2419                }
2420        } // class alignBottomAction
2421
2422        class centerHorizontalAction extends alignAction {
2423                /** for serialization */
2424                static final long serialVersionUID = 1;
2425                public centerHorizontalAction(FastVector nodes) {
2426                        super(nodes);
2427                } // c'tor
2428
2429                public void redo() {
2430                        try {
2431                                centerHorizontal(m_nodes);
2432                        } catch (Exception e) {
2433                                e.printStackTrace();
2434                        }
2435                } // redo
2436
2437                public String getUndoMsg() {
2438                        return "Returning " + m_nodes.size() + " from centering horizontally.";
2439                }
2440
2441                public String getRedoMsg() {
2442                        return "Centering " + m_nodes.size() + " nodes horizontally.";
2443                }
2444        } // class centerHorizontalAction
2445
2446        class centerVerticalAction extends alignAction {
2447                /** for serialization */
2448                static final long serialVersionUID = 1;
2449                public centerVerticalAction(FastVector nodes) {
2450                        super(nodes);
2451                } // c'tor
2452
2453                public void redo() {
2454                        try {
2455                                centerVertical(m_nodes);
2456                        } catch (Exception e) {
2457                                e.printStackTrace();
2458                        }
2459                } // redo
2460
2461                public String getUndoMsg() {
2462                        return "Returning " + m_nodes.size() + " from centering vertically.";
2463                }
2464
2465                public String getRedoMsg() {
2466                        return "Centering " + m_nodes.size() + " nodes vertically.";
2467                }
2468        } // class centerVerticalAction
2469
2470        class spaceHorizontalAction extends alignAction {
2471                /** for serialization */
2472                static final long serialVersionUID = 1;
2473                public spaceHorizontalAction(FastVector nodes) {
2474                        super(nodes);
2475                } // c'tor
2476
2477                public void redo() {
2478                        try {
2479                                spaceHorizontal(m_nodes);
2480                        } catch (Exception e) {
2481                                e.printStackTrace();
2482                        }
2483                } // redo
2484
2485                public String getUndoMsg() {
2486                        return "Returning " + m_nodes.size() + " from spaceing horizontally.";
2487                }
2488
2489                public String getRedoMsg() {
2490                        return "spaceing " + m_nodes.size() + " nodes horizontally.";
2491                }
2492        } // class spaceHorizontalAction
2493
2494        class spaceVerticalAction extends alignAction {
2495                /** for serialization */
2496                static final long serialVersionUID = 1;
2497                public spaceVerticalAction(FastVector nodes) {
2498                        super(nodes);
2499                } // c'tor
2500
2501                public void redo() {
2502                        try {
2503                                spaceVertical(m_nodes);
2504                        } catch (Exception e) {
2505                                e.printStackTrace();
2506                        }
2507                } // redo
2508
2509                public String getUndoMsg() {
2510                        return "Returning " + m_nodes.size() + " from spaceng vertically.";
2511                }
2512
2513                public String getRedoMsg() {
2514                        return "Spaceng " + m_nodes.size() + " nodes vertically.";
2515                }
2516        } // class spaceVerticalAction
2517
2518        class SetPositionAction extends UndoAction {
2519                /** for serialization */
2520                static final long serialVersionUID = 1;
2521                int m_nTargetNode;
2522
2523                int m_nX;
2524
2525                int m_nY;
2526
2527                int m_nX2;
2528
2529                int m_nY2;
2530
2531                SetPositionAction(int nTargetNode, int nX, int nY) {
2532                        m_nTargetNode = nTargetNode;
2533                        m_nX2 = nX;
2534                        m_nY2 = nY;
2535                        m_nX = getPositionX(nTargetNode);
2536                        m_nY = getPositionY(nTargetNode);
2537                } // c'tor
2538
2539                public void undo() {
2540                        setPosition(m_nTargetNode, m_nX, m_nY);
2541                } // undo
2542
2543                public void redo() {
2544                        setPosition(m_nTargetNode, m_nX2, m_nY2);
2545                } // redo
2546
2547                public void setUndoPosition(int nX, int nY) {
2548                        m_nX2 = nX;
2549                        m_nY2 = nY;
2550                } // setPosition
2551        } // class SetPositionAction
2552
2553        class SetGroupPositionAction extends UndoAction {
2554                /** for serialization */
2555                static final long serialVersionUID = 1;
2556                FastVector m_nodes;
2557                int m_dX;
2558                int m_dY;
2559
2560                SetGroupPositionAction(FastVector nodes, int dX, int dY) {
2561                        m_nodes = new FastVector(nodes.size());
2562                        for (int iNode = 0; iNode < nodes.size(); iNode++) {
2563                                m_nodes.addElement(nodes.elementAt(iNode));
2564                        }
2565                        m_dX = dX;
2566                        m_dY = dY;
2567                } // c'tor
2568
2569                public void undo() {
2570                        for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
2571                                int nNode = (Integer) m_nodes.elementAt(iNode);
2572                                setPosition(nNode, getPositionX(nNode) - m_dX,  getPositionY(nNode) - m_dY);
2573                        }
2574                } // undo
2575
2576                public void redo() {
2577                        for (int iNode = 0; iNode < m_nodes.size(); iNode++) {
2578                                int nNode = (Integer) m_nodes.elementAt(iNode);
2579                                setPosition(nNode, getPositionX(nNode) + m_dX,  getPositionY(nNode) + m_dY);
2580                        }
2581                } // redo
2582                public void setUndoPosition(int dX, int dY) {
2583                        m_dX += dX;
2584                        m_dY += dY;
2585                } // setPosition
2586        } // class SetGroupPositionAction
2587
2588        class LayoutGraphAction extends UndoAction {
2589                /** for serialization */
2590                static final long serialVersionUID = 1;
2591                FastVector m_nPosX;
2592                FastVector m_nPosY;
2593                FastVector m_nPosX2;
2594                FastVector m_nPosY2;
2595
2596                LayoutGraphAction(FastVector nPosX, FastVector nPosY) {
2597                        m_nPosX = new FastVector(nPosX.size());
2598                        m_nPosY = new FastVector(nPosX.size());
2599                        m_nPosX2 = new FastVector(nPosX.size());
2600                        m_nPosY2 = new FastVector(nPosX.size());
2601                        for (int iNode = 0; iNode < nPosX.size(); iNode++) {
2602                                m_nPosX.addElement(m_nPositionX.elementAt(iNode));
2603                                m_nPosY.addElement(m_nPositionY.elementAt(iNode));
2604                                m_nPosX2.addElement(nPosX.elementAt(iNode));
2605                                m_nPosY2.addElement(nPosY.elementAt(iNode));
2606                        }
2607                } // c'tor
2608
2609                public void undo() {
2610                        for (int iNode = 0; iNode < m_nPosX.size(); iNode++) {
2611                                setPosition(iNode, (Integer) m_nPosX.elementAt(iNode), (Integer) m_nPosY.elementAt(iNode));
2612                        }
2613                } // undo
2614
2615                public void redo() {
2616                        for (int iNode = 0; iNode < m_nPosX.size(); iNode++) {
2617                                setPosition(iNode, (Integer) m_nPosX2.elementAt(iNode), (Integer) m_nPosY2.elementAt(iNode));
2618                        }
2619                } // redo
2620        } // class LayoutGraphAction
2621
2622        class PasteAction extends UndoAction {
2623                /** for serialization */
2624                static final long serialVersionUID = 1;
2625                int m_nBase;
2626
2627                String m_sXML;
2628
2629                PasteAction(String sXML, int nBase) {
2630                        m_sXML = sXML;
2631                        m_nBase = nBase;
2632                } // c'tor
2633
2634                public void undo() {
2635                        try {
2636                                int iNode = getNrOfNodes() - 1;
2637                                while (iNode >= m_nBase) {
2638                                        deleteNode(iNode);
2639                                        iNode--;
2640                                }
2641                        } catch (Exception e) {
2642                                e.printStackTrace();
2643                        }
2644                } // undo
2645
2646                public void redo() {
2647                        try {
2648                                paste(m_sXML, EXECUTE);
2649                        } catch (Exception e) {
2650                                e.printStackTrace();
2651                        }
2652                } // redo
2653        } // class PasteAction
2654         
2655          /**
2656           * Returns the revision string.
2657           *
2658           * @return            the revision
2659           */
2660          public String getRevision() {
2661            return RevisionUtils.extract("$Revision: 4899 $");
2662          }
2663
2664        /**
2665         * @param args
2666         */
2667        public static void main(String[] args) {
2668        } // main
2669} // class EditableBayesNet
2670
Note: See TracBrowser for help on using the repository browser.