ai.aitia.meme.paramsweep.gui.Page_Recorders.java Source code

Java tutorial

Introduction

Here is the source code for ai.aitia.meme.paramsweep.gui.Page_Recorders.java

Source

/*******************************************************************************
 * Copyright (C) 2006-2013 AITIA International, Inc.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package ai.aitia.meme.paramsweep.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;
import org.xml.sax.SAXException;

import ai.aitia.meme.events.HybridAction;
import ai.aitia.meme.events.IHybridActionListener;
import ai.aitia.meme.gui.ExpandedEditor;
import ai.aitia.meme.gui.Wizard;
import ai.aitia.meme.gui.Wizard.Button;
import ai.aitia.meme.gui.Wizard.IArrowsInHeader;
import ai.aitia.meme.gui.Wizard.IWizardPage;
import ai.aitia.meme.paramsweep.ParameterSweepWizard;
import ai.aitia.meme.paramsweep.batch.IModelInformation.ModelInformationException;
import ai.aitia.meme.paramsweep.batch.output.RecordableInfo;
import ai.aitia.meme.paramsweep.generator.WizardSettingsManager;
import ai.aitia.meme.paramsweep.gui.info.GeneratedMemberInfo;
import ai.aitia.meme.paramsweep.gui.info.MemberInfo;
import ai.aitia.meme.paramsweep.gui.info.MultiColumnOperatorGeneratedMemberInfo;
import ai.aitia.meme.paramsweep.gui.info.ParameterInfo;
import ai.aitia.meme.paramsweep.gui.info.RecordableElement;
import ai.aitia.meme.paramsweep.gui.info.ResultInfo;
import ai.aitia.meme.paramsweep.gui.info.TimeInfo;
import ai.aitia.meme.paramsweep.gui.info.TimeInfo.Mode;
import ai.aitia.meme.paramsweep.gui.info.TimeInfo.WriteMode;
import ai.aitia.meme.paramsweep.internal.platform.IGUIController;
import ai.aitia.meme.paramsweep.internal.platform.IScriptSupport;
import ai.aitia.meme.paramsweep.internal.platform.InfoConverter;
import ai.aitia.meme.paramsweep.internal.platform.PlatformSettings;
import ai.aitia.meme.paramsweep.platform.ITestRunner;
import ai.aitia.meme.paramsweep.platform.PlatformManager;
import ai.aitia.meme.paramsweep.platform.PlatformManager.PlatformType;
import ai.aitia.meme.paramsweep.platform.repast.info.GeneratedRecordableInfo;
import ai.aitia.meme.paramsweep.utils.Util;
import ai.aitia.meme.paramsweep.utils.Utilities;
import ai.aitia.meme.paramsweep.utils.WizardLoadingException;
import ai.aitia.meme.utils.FormsUtils;
import ai.aitia.meme.utils.FormsUtils.Separator;
import ai.aitia.meme.utils.GUIUtils;
import ai.aitia.meme.utils.TestableDialog;
import ai.aitia.meme.utils.Utils;
import ai.aitia.meme.utils.Utils.Pair;

import com.jgoodies.forms.layout.CellConstraints;

/** This class provides the Data Collection page of the Parameter Sweep Wizard. */
public class Page_Recorders extends JPanel implements IWizardPage, IArrowsInHeader, ActionListener,
        ListSelectionListener, TreeSelectionListener, IHybridActionListener, CaretListener, ChangeListener {

    //===============================================================================
    // members

    private static final long serialVersionUID = 1L;

    /** The name of the types which are used as a type of a tick number. */
    private static List<String> iterationTypeNames = new ArrayList<String>(12);

    static {
        iterationTypeNames.add("byte");
        iterationTypeNames.add("Byte");
        iterationTypeNames.add("short");
        iterationTypeNames.add("Short");
        iterationTypeNames.add("int");
        iterationTypeNames.add("Integer");
        iterationTypeNames.add("long");
        iterationTypeNames.add("Long");
        iterationTypeNames.add("float");
        iterationTypeNames.add("Float");
        iterationTypeNames.add("double");
        iterationTypeNames.add("Double");
    }

    /** The owner of the page. */
    private ParameterSweepWizard owner = null;

    private HybridAction newRecorderAction = new HybridAction(this, "New recorder", null);
    private HybridAction editRecorderAction = new HybridAction(this, "Edit recorder", null);
    private HybridAction removeAction = new HybridAction(this, "Remove", null);

    /** Flag that determines whether the recorders tree are disabled or not. */
    private boolean disableRectree = false;

    /** The node that belongs to the edited recorder. */
    private DefaultMutableTreeNode editedRecorder = null;
    /** Reference to the Advanced dialog. */
    private WriteToFileDialog advancedDialog = null;
    private IScriptSupport scriptSupport = null;
    private boolean modelInformationException = false;
    private RecordableElement lastCreatedRecordableElement = null;

    //===============================================================================
    // GUI members

    private JPanel content = null;
    private JRadioButton iterationRadioButton = new JRadioButton("At given number of iteration");
    private JRadioButton conditionRadioButton = new JRadioButton("When conditon is true");
    private JLabel stopLabel = new JLabel("        Value: ");
    private JTextField stopField = new JTextField();
    private JButton stopEEButton = ExpandedEditor.makeEEButton(stopField, "Stop condition expression");
    private JPanel stopPanel = null;
    private DefaultMutableTreeNode root = new DefaultMutableTreeNode();
    private JTree recorderTree = new JTree(root);
    private JScrollPane recorderScr = new JScrollPane(recorderTree);
    private JButton newRecorderButton = new JButton(newRecorderAction);
    private JButton editRecorderButton = new JButton(editRecorderAction);
    private JButton removeButton = new JButton(removeAction);
    private JPanel treePanel = null;
    private JTextField nameField = new JTextField();
    private JTextField outputField = new JTextField();
    private JButton outputBrowseButton = new JButton("..");
    private JPanel generalPanel = null;
    private JRadioButton whenIterationButton = new JRadioButton("At the end of every iteration");
    private JRadioButton whenIterationIntervalButton = new JRadioButton("After every ");
    private JLabel wiiLabel = new JLabel(" iterations");
    private JRadioButton whenRunRadioButton = new JRadioButton("At the end of runs");
    private JRadioButton whenConditionRadioButton = new JRadioButton("When condition is true");
    private JTextField whenIterationIntervalField = new JTextField();
    private JLabel whenConditionLabel = new JLabel("        Condition: ");
    private JTextField whenConditionField = new JTextField();
    private JButton whenEEButton = ExpandedEditor.makeEEButton(whenConditionField, "Record condition expression");
    private JPanel whenPanel = null;
    private JButton advancedButton = new JButton("Advanced...");
    private JButton addRecorderButton = new JButton(" Add recorder ");
    private JButton cancelButton = new JButton("Cancel");
    private JTabbedPane recordableTabbedPane = new JTabbedPane();
    private JList recordableVariables = new JList();
    private JScrollPane recordableVariableScr = new JScrollPane(recordableVariables);
    private JList recordableMethods = new JList();
    private JScrollPane recordableMethodScr = new JScrollPane(recordableMethods);
    private JList recordableScripts = new JList();
    private JScrollPane recordableScriptScr = new JScrollPane(recordableScripts);
    private JList nonRecordableScripts = new JList();
    private JScrollPane nonRecordableScriptScr = new JScrollPane(nonRecordableScripts);
    private JButton addButton = new JButton("Add");
    private JButton addAsButton = new JButton("Add as...");
    private JButton createScriptButton = new JButton("Create");
    private JButton removeScriptButton = new JButton("Remove");
    private JButton editScriptButton = new JButton("Edit");
    private JButton testScriptButton = new JButton("Test");
    private JPanel recordablePanel = null;
    private JPanel editPanel = null;

    //===============================================================================
    // methods

    //-------------------------------------------------------------------------------
    /** Constructor.
     * @param owner owner of the page
     */
    public Page_Recorders(ParameterSweepWizard owner) {
        this.owner = owner;
        layoutGUI();
        initialize();
    }

    //-------------------------------------------------------------------------------
    /** Returns the root node of the recorders tree. */
    public DefaultMutableTreeNode getRecorders() {
        return root;
    }

    //----------------------------------------------------------------------------------------------------
    /** Returns false if the simulation stopping condition is a logical condition, true
     *  otherwise.
     */
    public boolean isStopAfterFixInterval() {
        return iterationRadioButton.isSelected();
    }

    //----------------------------------------------------------------------------------------------------
    /** Returns the content of the simulation stopping condition field (named as 'Value'
     *  or 'Condition' field).
     */
    public String getStopData() {
        return stopField.getText().trim();
    }

    //----------------------------------------------------------------------------------------------------
    public JTree getRecorderTree() {
        return recorderTree;
    }

    public JList getRecordableVariablesList() {
        return recordableVariables;
    }

    public JList getRecordableMethodsList() {
        return recordableMethods;
    }

    public int getSelectedRecordableTab() {
        return recordableTabbedPane.getSelectedIndex();
    }

    //----------------------------------------------------------------------------------------------------
    public void clearAllSelection() {
        recordableVariables.clearSelection();
        recordableMethods.clearSelection();
        recordableScripts.clearSelection();
        nonRecordableScripts.clearSelection();
    }

    //-------------------------------------------------------------------------------
    /** Initializes the three lists (variables/methods/scripts) of recordable elements. */
    public void initializeRecordableLists() {
        initializeRecordableLists(new ArrayList<ParameterInfo>(0));
    }

    //-------------------------------------------------------------------------------
    /** Initializes the three lists (variables/methods/scripts) of recordable elements.
     * @param illegalParameters the list of information objects of ceased (new) parameters. 
     */
    public void initializeRecordableLists(List<ParameterInfo> illegalParameters) {
        List<RecordableInfo> originalRecordables = null;
        try {
            originalRecordables = owner.getModelInformation().getRecordables();
        } catch (ModelInformationException e) {
            Utilities.userAlert(owner, "Identification of recordable elements is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
            return;
        }
        if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()) {
            if (scriptSupport == null)
                scriptSupport = PlatformSettings.getScriptSupport(this.owner, this);
            DefaultListModel[] listModels = scriptSupport.initializeScriptSupport(illegalParameters);
            recordableScripts.setModel(listModels[0]);
            nonRecordableScripts.setModel(listModels[1]);
        }
        DefaultListModel rVModel = new DefaultListModel();
        DefaultListModel rMModel = new DefaultListModel();
        for (RecordableInfo ri : originalRecordables) {
            if (isNewParameter(ri))
                continue;
            if (InfoConverter.isVariable(ri)) // variable
                rVModel.addElement(InfoConverter.recordableInfo2RecordableElement(ri));
            else // method
                rMModel.addElement(InfoConverter.recordableInfo2RecordableElement(ri));
        }
        recordableVariables.setModel(rVModel);
        recordableMethods.setModel(rMModel);
        recordableScripts.getModel().addListDataListener(new ListDataListener() {
            @Override
            public void intervalRemoved(ListDataEvent e) {
            }

            @Override
            public void intervalAdded(ListDataEvent e) {
                if (e.getIndex0() == e.getIndex1()) {
                    //single element added
                    lastCreatedRecordableElement = (RecordableElement) recordableScripts.getModel()
                            .getElementAt(e.getIndex0());
                }
            }

            @Override
            public void contentsChanged(ListDataEvent e) {
            }
        });
        if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()) {
            if (recordableTabbedPane.getTabCount() != 4) {
                recordableTabbedPane.addTab("Data sources", recordableScriptScr);
                recordableTabbedPane.addTab("Misc.", nonRecordableScriptScr);
                recordableTabbedPane.setToolTipTextAt(2,
                        "Recordable calculated derivative values (e.g. statistics).");
                recordableTabbedPane.setToolTipTextAt(3,
                        "<html>Non-recordable calculated derivative values (e.g. statistics). These<br>can't"
                                + " be used as a recordable element only in other data sources.<br>In this page"
                                + " you can edit or remove them.</html>");

            }
        } else {
            if (recordableTabbedPane.getTabCount() > 2) {
                recordableTabbedPane.removeTabAt(3);
                recordableTabbedPane.removeTabAt(2);
            }
        }
    }

    //   public void initalizeRecorders(){
    //      @SuppressWarnings("unchecked")
    //      DefaultListModel<RecordableElement> model = (DefaultListModel<RecordableElement>)recordableScripts.getModel();
    //      try {
    //         List<RecorderInfo> recorders = owner.getModelInformation().getRecorders();
    //         for (RecorderInfo recorderInfo : recorders) {
    //            DefaultMutableTreeNode recorder = new DefaultMutableTreeNode(recorderInfo.getName());
    //            ResultInfo ri = new ResultInfo(recorderInfo.getOutputFile().getName());
    //            DefaultMutableTreeNode riNode = new DefaultMutableTreeNode(ri);
    //            recorder.add(riNode);
    //            String recordType = recorderInfo.getRecordType();
    //            String arg = null;
    //            int indexOfColon = recordType.indexOf(':');
    //            if (indexOfColon != -1){
    //               arg = recordType.substring(indexOfColon + 1).trim();
    //               recordType = recordType.substring(0, indexOfColon).trim();
    //            }
    //            TimeInfo ti = new TimeInfo(recordType, arg);
    //            
    //            String writeType = recorderInfo.getWriteType();
    //            long writeArg = 0;
    //            indexOfColon = writeType.indexOf(':');
    //            if (indexOfColon != -1){
    //               writeArg = Long.parseLong(writeType.substring(indexOfColon + 1));
    //               writeType = writeType.substring(0, indexOfColon).trim();
    //            }
    //            ti.setWriteMode(writeType, writeArg);
    //            DefaultMutableTreeNode tiNode = new DefaultMutableTreeNode(ti);
    //            recorder.add(tiNode);
    //            DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
    //            treeModel.insertNodeInto(recorder,root,root.getChildCount());
    //
    //            List<RecordableInfo> recordables = recorderInfo.getRecordables();
    //            for (RecordableInfo recordableInfo : recordables) {
    //               RecordableElement recordableElement = InfoConverter.recordableInfo2RecordableElement(recordableInfo);
    //               if (!model.contains(recordableElement)){
    //                  model.addElement(recordableElement);
    //               }
    //               if (!(MultiColumnOperatorGeneratedMemberInfo.class.isAssignableFrom(recordableElement.getInfo().getClass()))
    //                     && (Collection.class.isAssignableFrom(recordableElement.getInfo().getJavaType()) || recordableElement.getInfo().getJavaType().isArray())) {
    //                  scriptSupport.createMultiColumnScript(recordableElement.getInfo(), (DefaultListModel)recordableScripts.getModel(),(DefaultListModel)nonRecordableScripts.getModel(), null);
    //                  //look for the newly created recordable and add it to the recorder
    //                  if (lastCreatedRecordableElement != null) {
    //                     if (!contains(recorder, lastCreatedRecordableElement.getInfo())) {
    //                        treeModel.insertNodeInto(new DefaultMutableTreeNode(lastCreatedRecordableElement),recorder,recorder.getChildCount());
    //                     }
    //                     lastCreatedRecordableElement = null;
    //                  }
    //               } else if (!contains(recorder,recordableElement.getInfo())) {
    //                  treeModel.insertNodeInto(new DefaultMutableTreeNode(recordableElement),recorder,recorder.getChildCount());
    //               }
    //               scriptSupport.getAllMembers().add(recordableElement.getInfo());
    //               Fugg.f.setFuggveny(recordableElement.getInfo().getName() + " : " + recordableElement.getInfo().getType(),"");
    //
    //            }
    //            
    //
    //            TreePath path = new TreePath(recorder.getPath());
    //            recorderTree.setSelectionPath(path);
    //            recorderTree.expandPath(path);
    //
    //         }
    //      } catch (ModelInformationException e) {
    //         Utilities.userAlert(owner,"Identification of pre-configured recorders is failed:",Util.getLocalizedMessage(e));
    //         modelInformationException = true;
    //         owner.enableDisableButtons();
    //         owner.setModelState(ParameterSweepWizard.ERROR);
    //         e.printStackTrace(ParameterSweepWizard.getLogStream());
    //         return;
    //      }
    //   }

    public void initalizeRecorders() {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(owner.getCustomClassLoader());
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder parser = factory.newDocumentBuilder();
            String recordersXML = owner.getModelInformation().getRecordersXML();
            if (recordersXML != null) {
                Document document = parser.parse(new ByteArrayInputStream(recordersXML.getBytes("UTF-8")));

                Element rootElement = document.getDocumentElement();

                load(rootElement);
            }
        } catch (ModelInformationException e) {
            Utilities.userAlert(owner, "Identification of pre-configured recorders is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
            return;
        } catch (ParserConfigurationException e) {
            Utilities.userAlert(owner, "Identification of pre-configured recorders is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
        } catch (SAXException e) {
            Utilities.userAlert(owner, "Identification of pre-configured recorders is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
        } catch (IOException e) {
            Utilities.userAlert(owner, "Identification of pre-configured recorders is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
        } catch (WizardLoadingException e) {
            Utilities.userAlert(owner, "Identification of pre-configured recorders is failed:",
                    Util.getLocalizedMessage(e));
            modelInformationException = true;
            owner.enableDisableButtons();
            owner.setModelState(ParameterSweepWizard.ERROR);
            e.printStackTrace(ParameterSweepWizard.getLogStream());
        } finally {
            Thread.currentThread().setContextClassLoader(contextClassLoader);
        }
    }

    //-------------------------------------------------------------------------------
    /** Deletes all illegal recordable elements from the recorders. */
    public void cleanRecorders() {
        List<String> reserved = Arrays.asList(owner.getInitParam());
        DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
        for (int i = 0; i < root.getChildCount(); ++i) {
            List<DefaultMutableTreeNode> illegal = new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode recorderNode = (DefaultMutableTreeNode) root.getChildAt(i);
            if (recorderNode.getChildCount() > 2) {
                for (int j = 2; j < recorderNode.getChildCount(); ++j) {
                    DefaultMutableTreeNode recordable = (DefaultMutableTreeNode) recorderNode.getChildAt(j);
                    RecordableElement re = (RecordableElement) recordable.getUserObject();
                    MemberInfo mi = re.getInfo();
                    if (reserved.contains(mi.getName()))
                        illegal.add(recordable);
                    else if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()) {
                        if (scriptSupport.getIllegalGenerateds() != null && mi instanceof GeneratedMemberInfo) {
                            GeneratedMemberInfo gmi = (GeneratedMemberInfo) mi;
                            if (scriptSupport.getIllegalGenerateds().contains(gmi))
                                illegal.add(recordable);
                        }
                    }
                }
            }
            for (DefaultMutableTreeNode node : illegal)
                treeModel.removeNodeFromParent(node);
        }
        if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport())
            scriptSupport.illegalGeneratedsToNull();
        owner.enableDisableButtons();
    }

    //-------------------------------------------------------------------------------
    /** Saves the page-related model settings to the XML node <code>node</code>. This
     *  method is part of the model settings storing performed by {@link ai.aitia.meme.paramsweep.generator.WizardSettingsManager 
     *  WizardSettingsManager}.
     */
    public void save(Node node) {
        Document document = node.getOwnerDocument();

        Element element = document.createElement(WizardSettingsManager.STOP_DATA);
        element.setAttribute(WizardSettingsManager.IS_CONDITION, String.valueOf(!isStopAfterFixInterval()));
        element.appendChild(document.createTextNode(stopField.getText().trim()));
        node.appendChild(element);

        if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport())
            scriptSupport.saveScripts(node);

        Element recorders = document.createElement(WizardSettingsManager.RECORDERS);
        for (int i = 0; i < root.getChildCount(); ++i) {
            DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode) root.getChildAt(i);
            Element recorder = document.createElement(WizardSettingsManager.RECORDER);
            recorder.setAttribute(WizardSettingsManager.NAME, treeNode.toString());

            DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) treeNode.getFirstChild();
            ResultInfo ri = (ResultInfo) childNode.getUserObject();
            element = document.createElement(WizardSettingsManager.OUTPUT_FILE);
            element.appendChild(document.createTextNode(ri.getFile()));
            recorder.appendChild(element);

            childNode = (DefaultMutableTreeNode) treeNode.getChildAt(1);
            TimeInfo ti = (TimeInfo) childNode.getUserObject();
            element = document.createElement(WizardSettingsManager.TIME);
            element.setAttribute(WizardSettingsManager.TYPE, ti.getType().toString());
            if (ti.getArg() != null)
                element.appendChild(document.createTextNode(ti.getArg()));
            recorder.appendChild(element);

            element = document.createElement(WizardSettingsManager.WRITING_TIME);
            element.setAttribute(WizardSettingsManager.TYPE, ti.getWriteType().toString());
            if (ti.getWriteArg() > 0)
                element.appendChild(document.createTextNode(String.valueOf(ti.getWriteArg())));
            recorder.appendChild(element);
            InfoConverter.saveRecorderElementsToXml(recorder, treeNode);
            recorders.appendChild(recorder);
        }
        node.appendChild(recorders);
    }

    //-------------------------------------------------------------------------------
    /** Loads the page-related model settings from the XML element <code>element</code>. This
     *  method is part of the model settings retrieving performed by {@link ai.aitia.meme.paramsweep.generator.WizardSettingsManager 
     *  WizardSettingsManager}.<br>
     *  Pre-condition : <code>Page_LoadModel.load()</code> has finished
     * @throws WizardLoadingException if the XML document is invalid
     */
    public void load(Element element) throws WizardLoadingException {
        if (scriptSupport == null && PlatformSettings.getGUIControllerForPlatform().isScriptingSupport())
            scriptSupport = PlatformSettings.getScriptSupport(this.owner, this);
        initializeRecordableLists();

        NodeList nl = element.getElementsByTagName(WizardSettingsManager.STOP_DATA);
        if (nl == null || nl.getLength() == 0)
            throw new WizardLoadingException(true, "missing node: " + WizardSettingsManager.STOP_DATA);
        Element stopElement = (Element) nl.item(0);
        String condition = stopElement.getAttribute(WizardSettingsManager.IS_CONDITION);
        if (condition.equals(""))
            throw new WizardLoadingException(true,
                    "missing 'condition' attribute at node: " + WizardSettingsManager.STOP_DATA);
        if (Boolean.parseBoolean(condition))
            conditionRadioButton.setSelected(true);
        else
            iterationRadioButton.setSelected(true);
        NodeList content = stopElement.getChildNodes();
        if (content != null && content.getLength() != 0) {
            String value = ((Text) content.item(0)).getNodeValue();
            stopField.setText(value);
        }

        if (PlatformSettings.getGUIControllerForPlatform().isScriptingSupport())
            scriptSupport.createScriptList(element, (DefaultListModel) recordableScripts.getModel(),
                    (DefaultListModel) nonRecordableScripts.getModel());

        nl = null;
        nl = element.getElementsByTagName(WizardSettingsManager.RECORDERS);
        if (nl == null || nl.getLength() == 0)
            throw new WizardLoadingException(true, "missing node: " + WizardSettingsManager.RECORDERS);
        Element recorders = (Element) nl.item(0);
        createRecorderTree(recorders);
    }

    //----------------------------------------------------------------------------------------------------
    public void removeFromRecorders(GeneratedMemberInfo info) {
        DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
        for (int i = 0; i < root.getChildCount(); ++i) {
            List<DefaultMutableTreeNode> illegal = new ArrayList<DefaultMutableTreeNode>();
            DefaultMutableTreeNode recorderNode = (DefaultMutableTreeNode) root.getChildAt(i);
            if (recorderNode.getChildCount() > 2) {
                for (int j = 2; j < recorderNode.getChildCount(); ++j) {
                    DefaultMutableTreeNode recordable = (DefaultMutableTreeNode) recorderNode.getChildAt(j);
                    RecordableElement re = (RecordableElement) recordable.getUserObject();
                    MemberInfo mi = re.getInfo();
                    if (mi.equals(info))
                        illegal.add(recordable);
                }
            }
            for (DefaultMutableTreeNode node : illegal)
                treeModel.removeNodeFromParent(node);
        }
    }

    //----------------------------------------------------------------------------------------------------
    public void removeFromStoppingCondition(GeneratedMemberInfo info) {
        String candidate = new String(stopField.getText().trim());
        if ("".equals(candidate))
            return;
        if (iterationRadioButton.isSelected()) { // fix iteration
            if (!candidate.endsWith("()"))
                candidate += "()";
            MemberInfo candidateInfo = new MemberInfo(candidate, "", null);
            if (candidateInfo.equals(info))
                stopField.setText("");
        } else {
            if (candidate.contains(info.getName()))
                stopField.setText("");
        }
    }

    //-------------------------------------------------------------------------------
    /** Checks whether the recorders tree is valid or not. */
    public boolean isTreeValid() {
        return root.getChildCount() > 0;
    }

    public void setStopFieldText(String text) {
        stopField.setText(text);
    }

    //-------------------------------------------------------------------------------
    public IScriptSupport getScriptSupport() {
        if (scriptSupport == null)
            scriptSupport = PlatformSettings.getScriptSupport(this.owner, this);

        return scriptSupport;
    }

    //===============================================================================
    // interface implementations

    //-------------------------------------------------------------------------------
    public String getInfoText(Wizard w) {
        return w.getArrowsHeader("Specify data collection");
    }

    public Container getPanel() {
        return this;
    }

    public String getTitle() {
        return "Data collection";
    }

    public boolean isEnabled(Button b) {
        if (modelInformationException && (b == Button.NEXT || b == Button.FINISH))
            return false;
        return PlatformSettings.isEnabledForPageRecorders(owner, b);
    }

    //-------------------------------------------------------------------------------
    public boolean onButtonPress(Button b) {
        if (b == Button.FINISH || b == Button.CUSTOM) {
            boolean[] checkThis = PlatformSettings.finishCheck(owner, this); // inspired by Speak
            if (!checkThis[0])
                return false;
            if (checkThis[1]) {
                List<String> emptyRecorder = emptyRecorders();
                if (emptyRecorder.size() > 0) {
                    int result = Utilities.askUserOK(ParameterSweepWizard.getFrame(), "Warning",
                            "The following data recorders do not contain any" + "recordable elements:",
                            Utils.join(emptyRecorder, ", ") + ".", "Click 'OK' to continue anyway.",
                            "If you would like to add recordable elements to these"
                                    + " data recorders click 'Cancel'.");
                    if (result == 0)
                        return false;
                }

                List<String> invalidRecorders = invalidRecorderPath();
                if (invalidRecorders.size() > 0) {
                    Utilities.userAlert(ParameterSweepWizard.getFrame(),
                            "Invalid output path(s) at the following data recorder(s):",
                            Utils.join(invalidRecorders, ", ") + ".", "",
                            "At distributed simulation running you can only define the name of the result files.");
                    return false;
                }
                return checkEndOfSimulation();
            }
            return true;
        } else if (b == Button.BACK) {
            if (isEnabled(b) && owner.getSweepingMethodID() != 0)
                owner.gotoPage(3);
        }
        return isEnabled(b);
    }

    //-------------------------------------------------------------------------------
    public void onPageChange(boolean show) {
        if (show) {
            if (owner.getModelState() == ParameterSweepWizard.ERROR)
                return;
            initializePageForPlatform();
            if (owner.getModelState() == ParameterSweepWizard.NEW_RECORDERS) {
                modelInformationException = false;
                resetPage();
                if (scriptSupport == null && PlatformSettings.getGUIControllerForPlatform().isScriptingSupport())
                    scriptSupport = PlatformSettings.getScriptSupport(this.owner, this);
                initializeRecordableLists();
                initalizeRecorders();
                owner.setModelState(ParameterSweepWizard.OLD);
            }
            if (root.getChildCount() == 0) {
                newRecorderButton.doClick();
                Thread hackThread = new Thread(new Runnable() {
                    // do this because <component>.grabFocus() does nothing if the <component> is not visible
                    Object o = new Object();

                    public void run() {
                        try {
                            synchronized (o) {
                                o.wait(1);
                            }
                        } catch (InterruptedException e) {
                        }
                        nameField.grabFocus();
                    }
                });
                hackThread.setName("Support-Thread-0");
                hackThread.start();
            }
        } else if (cancelButton.isEnabled())
            cancelButton.doClick();
    }

    //-------------------------------------------------------------------------------
    public void actionPerformed(ActionEvent e) {
        hybridAction(e, null);
    }

    //-------------------------------------------------------------------------------
    /** This method is intended to be a common place for handling "related" actionPerformed()
     *  messages (here "related" usually means that belong to the same window).
     * @param a Non-null only if this actionPerformed() message is originated from a
     *           HybridAction object.     
     */
    public void hybridAction(ActionEvent e, HybridAction a) {
        if (a != null) {
            if (a == newRecorderAction) {
                resetSettings();
                enableDisableSettings(true);
                disableRecTree();
                String defaultName = generateName();
                nameField.setText(defaultName);
                if (owner.getModelFileName() != null) {
                    String defaultFile = defaultName + ".txt";
                    outputField.setText(defaultFile);
                    outputField.setToolTipText(defaultFile);
                    addRecorderButton.setEnabled(true);
                    advancedDialog = new WriteToFileDialog(ParameterSweepWizard.getFrame(), WriteMode.RUN);
                }
                nameField.grabFocus();
            } else if (a == editRecorderAction) {
                TreePath path = recorderTree.getSelectionPath();
                if (path == null)
                    return;
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
                while (!(node.getUserObject() instanceof String)) {
                    if (node.equals(root))
                        return;
                    node = (DefaultMutableTreeNode) node.getParent();
                }
                resetSettings();
                enableDisableSettings(true);
                disableRecTree();
                addRecorderButton.setText("Modify recorder");
                edit(node);
            } else if (a == removeAction) {
                TreePath path = recorderTree.getSelectionPath();
                if (path == null)
                    return;
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();

                DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
                if (node.getUserObject() instanceof RecordableElement) {
                    treeModel.removeNodeFromParent(node);
                } else {
                    while (!(node.getUserObject() instanceof String)) {
                        if (node.equals(root))
                            return;
                        node = (DefaultMutableTreeNode) node.getParent();
                    }
                    int result = Utilities.askUser(owner, false, "Delete recorder",
                            "Do you really want to delete this recorder: " + node.toString() + "?");
                    if (result == 1)
                        treeModel.removeNodeFromParent(node);
                }
                owner.enableDisableButtons();
            }
        } else {
            String command = e.getActionCommand();
            if (command.equals("STOP_ITERATION") || command.equals("STOP_CONDITION")) {
                setStopLabel(command);
                stopEEButton.setEnabled(!stopEEButton.isEnabled());
                stopField.setText("");
                stopField.grabFocus();
            } else if ("OUTPUT_BROWSE".equals(command)) {
                File original = null;
                if (outputField.getText().trim().length() != 0) {
                    original = new File(outputField.getText().trim());
                    if (!original.exists() || original.isDirectory())
                        original = null;
                }
                JFileChooser chooser = new JFileChooser(
                        original == null ? ParameterSweepWizard.getLastDir() : original);
                if (original != null)
                    chooser.setSelectedFile(original);
                chooser.setAcceptAllFileFilterUsed(true);
                int result = chooser.showSaveDialog(this);
                File f = null;
                if (result == JFileChooser.APPROVE_OPTION) {
                    f = chooser.getSelectedFile();
                    ParameterSweepWizard.setLastDir(f);
                    if (f.exists()) {
                        int ans = Utilities.askUser(this, false, "Override comfirmation",
                                String.format("%s is exists.", f.getName()), "Are you sure?");
                        if (ans == 0)
                            return;
                    }
                    outputField.setText(f.getAbsolutePath());
                    outputField.setToolTipText(f.getAbsolutePath());
                    addRecorderButton.setEnabled(nameField.getText().trim().length() != 0);
                }
            } else if (command.equals("WHEN_ITERATION") || command.equals("WHEN_RUN")) {
                whenIterationIntervalField.setEnabled(false);
                whenIterationIntervalField.setText("");
                whenConditionField.setEnabled(false);
                whenConditionField.setText("");
                whenEEButton.setEnabled(false);
            } else if (command.equals("WHEN_ITERATION_INTERVAL")) {
                whenIterationIntervalField.setEnabled(true);
                whenIterationIntervalField.grabFocus();
                whenConditionField.setEnabled(false);
                whenConditionField.setText("");
                whenEEButton.setEnabled(false);
            } else if (command.equals("WHEN_CONDITION")) {
                whenIterationIntervalField.setEnabled(false);
                whenIterationIntervalField.setText("");
                whenConditionField.setEnabled(true);
                whenConditionField.grabFocus();
                whenEEButton.setEnabled(true);
            } else if (command.equals("ADD_OR_MODIFY")) {
                boolean wrongName = false;
                if (editedRecorder == null)
                    wrongName = !isNewName(nameField.getText().trim());
                else
                    wrongName = !isNewName(nameField.getText().trim())
                            && !editedRecorder.toString().equals(nameField.getText().trim());
                if (wrongName) {
                    Utilities.userAlert(owner, "This name is already assigned to another recorder.");
                    return;
                }
                String[] error = new String[1];
                TimeInfo ti = getTimeInfo(error);
                if (ti == null) {
                    Utilities.userAlert(owner, error[0]);
                    return;
                }
                applyAdvancedSettings(ti);
                String text = outputField.getText().trim();
                if (isAlreadyAssigned(text)) {
                    Utilities.userAlert(owner, "This file is already assigned to another recorder.");
                    return;
                }

                final File recFile = new File(text);
                if (!isValidRecorderFile(recFile.getName())) {
                    Utilities.userAlert(owner,
                            "The name of the output file is invalid (only alphanumeric characters are allowed).");
                    return;
                }

                if (addRecorderButton.getText().startsWith("Modify")) {
                    editedRecorder.setUserObject(new String(nameField.getText().trim()));
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) editedRecorder.getFirstChild();
                    node.setUserObject(new ResultInfo(text));
                    node = (DefaultMutableTreeNode) editedRecorder.getChildAt(1);
                    node.setUserObject(ti);
                    TreePath path = new TreePath(editedRecorder.getPath());
                    recorderTree.setSelectionPath(path);
                    recorderTree.expandPath(path);
                    editedRecorder = null;
                    fireTreeNodesChanged(path);
                } else {
                    DefaultMutableTreeNode recorder = new DefaultMutableTreeNode(nameField.getText().trim());
                    ResultInfo ri = new ResultInfo(text);
                    DefaultMutableTreeNode riNode = new DefaultMutableTreeNode(ri);
                    recorder.add(riNode);
                    DefaultMutableTreeNode tiNode = new DefaultMutableTreeNode(ti);
                    recorder.add(tiNode);
                    DefaultTreeModel model = (DefaultTreeModel) recorderTree.getModel();
                    model.insertNodeInto(recorder, root, root.getChildCount());
                    TreePath path = new TreePath(recorder.getPath());
                    recorderTree.setSelectionPath(path);
                    recorderTree.expandPath(path);
                }
                resetSettings();
                enableDisableSettings(false);
                addRecorderButton.setEnabled(false);
                disableRectree = false;
                newRecorderAction.setEnabled(true);
                removeAction.setEnabled(true);
                editRecorderAction.setEnabled(true);
                owner.enableDisableButtons();
                advancedDialog = null;
            } else if (command.equals("CANCEL")) {
                resetSettings();
                enableDisableSettings(false);
                addRecorderButton.setEnabled(false);
                disableRectree = false;
                newRecorderAction.setEnabled(true);
                TreePath path = recorderTree.getSelectionPath();
                if (path != null) {
                    DefaultMutableTreeNode selected = (DefaultMutableTreeNode) path.getLastPathComponent();
                    if (selected != null) {
                        removeAction.setEnabled(true);
                        editRecorderAction.setEnabled(true);
                    }
                }
            } else {
                IGUIController controller = PlatformSettings.getGUIControllerForPlatform();
                if (command.equals("ADD")) {
                    DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) recorderTree.getSelectionPath()
                            .getLastPathComponent();

                    if (disableRectree) {
                        node = editedRecorder;
                        recorderTree.setSelectionPath(new TreePath(node.getPath()));
                    }

                    while (!(node.getUserObject() instanceof String)) {
                        if (node.equals(root))
                            return;
                        node = (DefaultMutableTreeNode) node.getParent();
                    }

                    DefaultListModel listModel = (DefaultListModel) recordableVariables.getModel();
                    int[] indices = recordableVariables.getSelectedIndices();

                    JPopupMenu pMenu = controller.getAddPopupMenu(owner);
                    if (pMenu != null) {
                        pMenu.setName("cmenu_wizard_record_recordablescmenu");
                        if (indices.length > 0 && recordableTabbedPane.getSelectedIndex() == 0) {
                            RecordableElement re = (RecordableElement) listModel.get(indices[0]);
                            controller.enabledDisabledPopupMenuElements(re);
                            int menuX = addButton.getX() + addButton.getWidth() + recordablePanel.getX()
                                    + editPanel.getX();
                            int menuY = addButton.getY() + addButton.getHeight() + recordablePanel.getY()
                                    + editPanel.getY();
                            pMenu.show(content, menuX, menuY);
                        }

                        listModel = (DefaultListModel) recordableMethods.getModel();
                        indices = recordableMethods.getSelectedIndices();

                        if (indices.length > 0 && recordableTabbedPane.getSelectedIndex() == 1) {
                            RecordableElement re = (RecordableElement) listModel.get(indices[0]);
                            controller.enabledDisabledPopupMenuElements(re);
                            int menuX = addButton.getX() + addButton.getWidth() + recordablePanel.getX()
                                    + editPanel.getX();
                            int menuY = addButton.getY() + addButton.getHeight() + recordablePanel.getY()
                                    + editPanel.getY();
                            pMenu.show(content, menuX, menuY);
                        }
                    } else {
                        for (int index : indices) {
                            RecordableElement re = (RecordableElement) listModel.get(index);
                            if (Collection.class.isAssignableFrom(re.getInfo().getJavaType())
                                    || re.getInfo().getJavaType().isArray()) {
                                scriptSupport.createMultiColumnScript(re.getInfo(),
                                        (DefaultListModel) recordableScripts.getModel(),
                                        (DefaultListModel) nonRecordableScripts.getModel(), null);
                                //look for the newly created recordable and add it to the recorder
                                if (lastCreatedRecordableElement != null) {
                                    if (!contains(node, lastCreatedRecordableElement.getInfo())) {
                                        treeModel.insertNodeInto(
                                                new DefaultMutableTreeNode(lastCreatedRecordableElement), node,
                                                node.getChildCount());
                                    }
                                    lastCreatedRecordableElement = null;
                                }
                            } else if (!contains(node, re.getInfo()))
                                treeModel.insertNodeInto(new DefaultMutableTreeNode(re), node,
                                        node.getChildCount());
                        }

                        listModel = (DefaultListModel) recordableMethods.getModel();
                        indices = recordableMethods.getSelectedIndices();
                        for (int index : indices) {
                            RecordableElement re = (RecordableElement) listModel.get(index);
                            if (Collection.class.isAssignableFrom(re.getInfo().getJavaType())
                                    || re.getInfo().getJavaType().isArray()) {
                                scriptSupport.createMultiColumnScript(re.getInfo(),
                                        (DefaultListModel) recordableScripts.getModel(),
                                        (DefaultListModel) nonRecordableScripts.getModel(), null);
                                //look for the newly created recordable and add it to the recorder
                                if (lastCreatedRecordableElement != null) {
                                    if (!contains(node, lastCreatedRecordableElement.getInfo())) {
                                        treeModel.insertNodeInto(
                                                new DefaultMutableTreeNode(lastCreatedRecordableElement), node,
                                                node.getChildCount());
                                    }
                                    lastCreatedRecordableElement = null;
                                }
                            } else if (!contains(node, re.getInfo())) {
                                treeModel.insertNodeInto(new DefaultMutableTreeNode(re), node,
                                        node.getChildCount());
                            }
                        }

                        if (controller.isScriptingSupport()) {
                            listModel = (DefaultListModel) recordableScripts.getModel();
                            indices = recordableScripts.getSelectedIndices();
                            for (int index : indices) {
                                RecordableElement re = (RecordableElement) listModel.get(index);
                                if (!(MultiColumnOperatorGeneratedMemberInfo.class
                                        .isAssignableFrom(re.getInfo().getClass()))
                                        && (Collection.class.isAssignableFrom(re.getInfo().getJavaType())
                                                || re.getInfo().getJavaType().isArray())) {
                                    scriptSupport.createMultiColumnScript(re.getInfo(),
                                            (DefaultListModel) recordableScripts.getModel(),
                                            (DefaultListModel) nonRecordableScripts.getModel(), null);
                                    //look for the newly created recordable and add it to the recorder
                                    if (lastCreatedRecordableElement != null) {
                                        if (!contains(node, lastCreatedRecordableElement.getInfo())) {
                                            treeModel.insertNodeInto(
                                                    new DefaultMutableTreeNode(lastCreatedRecordableElement), node,
                                                    node.getChildCount());
                                        }
                                        lastCreatedRecordableElement = null;
                                    }
                                } else if (!contains(node, re.getInfo())) {
                                    treeModel.insertNodeInto(new DefaultMutableTreeNode(re), node,
                                            node.getChildCount());
                                }
                            }
                            recordableMethods.clearSelection();
                            recordableScripts.clearSelection();
                        }
                        recordableVariables.clearSelection();
                        nonRecordableScripts.clearSelection();
                        owner.enableDisableButtons();
                    }
                } else if ("ADD_AS".equals(command)) {
                    DefaultTreeModel treeModel = (DefaultTreeModel) recorderTree.getModel();
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) recorderTree.getSelectionPath()
                            .getLastPathComponent();

                    if (disableRectree) {
                        node = editedRecorder;
                        recorderTree.setSelectionPath(new TreePath(node.getPath()));
                    }

                    while (!(node.getUserObject() instanceof String)) {
                        if (node.equals(root))
                            return;
                        node = (DefaultMutableTreeNode) node.getParent();
                    }

                    RecordableElement selected = (RecordableElement) recordableVariables.getSelectedValue();
                    if (selected == null)
                        selected = (RecordableElement) recordableMethods.getSelectedValue();
                    if (selected == null && controller.isScriptingSupport())
                        selected = (RecordableElement) recordableScripts.getSelectedValue();

                    if (selected != null) {
                        if (contains(node, selected.getInfo())) {
                            Utilities.userAlert(owner,
                                    "The selected recordable element " + selected.getInfo().toString()
                                            + " is already contained by" + " the selected recorder.");
                            return;
                        }
                        /*String alias = (String) JOptionPane.showInputDialog(owner,"Alias name for " + selected.getInfo().toString() + ":","Add as...",
                                                     JOptionPane.PLAIN_MESSAGE,null,null,selected.getNameForRecorders());*/

                        String alias = (String) TestableDialog.showInputDialog(owner,
                                "Alias name for " + selected.getInfo().toString() + ":", "Add as...",
                                JOptionPane.PLAIN_MESSAGE, null, null, selected.getNameForRecorders(),
                                "dial_wizard_record_addas");

                        /*String alias2 = (String) JOptionPane.showInputDialog(owner,"Alias name for " + selected.getInfo().toString() + ":","Add as...",
                                                     JOptionPane.PLAIN_MESSAGE,null,null,selected.getNameForRecorders());*/
                        if (null != alias && alias.trim().length() > 0) {
                            selected.setAlias(alias);
                            if (!(MultiColumnOperatorGeneratedMemberInfo.class
                                    .isAssignableFrom(selected.getInfo().getClass()))
                                    && (Collection.class.isAssignableFrom(selected.getInfo().getJavaType())
                                            || selected.getInfo().getJavaType().isArray())) {
                                scriptSupport.createMultiColumnScript(selected.getInfo(),
                                        (DefaultListModel) recordableScripts.getModel(),
                                        (DefaultListModel) nonRecordableScripts.getModel(), alias);
                                //look for the newly created recordable and add it to the recorder
                                if (lastCreatedRecordableElement != null) {
                                    if (!contains(node, lastCreatedRecordableElement.getInfo())) {
                                        treeModel.insertNodeInto(
                                                new DefaultMutableTreeNode(lastCreatedRecordableElement), node,
                                                node.getChildCount());
                                    }
                                    lastCreatedRecordableElement = null;
                                }
                            } else {
                                treeModel.insertNodeInto(new DefaultMutableTreeNode(selected), node,
                                        node.getChildCount());
                            }
                        }
                    }
                    recordableVariables.clearSelection();
                    recordableMethods.clearSelection();
                    if (controller.isScriptingSupport()) {
                        recordableScripts.clearSelection();
                        nonRecordableScripts.clearSelection();
                    }
                    owner.enableDisableButtons();
                } else if (command.equals("CREATE"))
                    scriptSupport.createScript((DefaultListModel) recordableScripts.getModel(),
                            (DefaultListModel) nonRecordableScripts.getModel());
                else if (command.equals("EDIT_SCRIPT")) {
                    RecordableElement selected = null;
                    if (recordableTabbedPane.getSelectedIndex() == 2)
                        selected = (RecordableElement) recordableScripts.getSelectedValue();
                    else if (recordableTabbedPane.getSelectedIndex() == 3)
                        selected = (RecordableElement) nonRecordableScripts.getSelectedValue();
                    recordableScripts.clearSelection();
                    nonRecordableScripts.clearSelection();
                    if (selected != null)
                        scriptSupport.editScript(selected, (DefaultListModel) recordableScripts.getModel(),
                                (DefaultListModel) nonRecordableScripts.getModel());
                } else if (command.equals("ADVANCED")) {
                    long arg = 0;
                    String error[] = new String[1];
                    TimeInfo ti = getTimeInfo(error);
                    if (error[0] != null) {
                        Utilities.userAlert(owner, error[0]);
                        return;
                    }
                    if (ti.getType() == Mode.ITERATION_INTERVAL)
                        arg = Long.parseLong(ti.getArg());
                    advancedDialog.showDialog(ti.getType(), arg);
                } else if (command.equals("REMOVE_SCRIPT"))
                    scriptSupport.removeScript(recordableScripts, nonRecordableScripts);
                else if (command.equals("TEST_SCRIPT")) {
                    List<GeneratedRecordableInfo> dataSources = new ArrayList<GeneratedRecordableInfo>();
                    for (Object o : recordableScripts.getSelectedValues()) {
                        RecordableElement re = (RecordableElement) o;
                        dataSources.add((GeneratedRecordableInfo) InfoConverter
                                .convertRecordableElement2RecordableInfo(re));
                    }
                    ITestRunner runner = (ITestRunner) PlatformManager
                            .getPlatform(PlatformSettings.getPlatformType());
                    String msg = "Everything seems ok.";
                    Throwable t = null;
                    try {
                        Pair<String, Throwable> result = runner.testRun(owner, dataSources);
                        if (result != null) {
                            msg = result.getFirst() != null
                                    ? "The following problem occured during the test:\n" + result.getFirst()
                                    : null;
                            t = result.getSecond();
                        }
                    } catch (IOException e1) {
                        msg = "I/O problem occured during the test:\n" + Util.getLocalizedMessage(e1);
                        t = e1;
                    }
                    NetLogoTestResultsDialog dlg = new NetLogoTestResultsDialog(ParameterSweepWizard.getFrame(),
                            msg, t);
                    dlg.showDialog();
                }
            }
        }
    }

    //------------------------------------------------------------------------------
    public void caretUpdate(CaretEvent e) {
        if (e.getSource().equals(nameField) || e.getSource().equals(outputField))
            addRecorderButton.setEnabled(
                    outputField.getText().trim().length() != 0 && nameField.getText().trim().length() != 0);
        else
            owner.enableDisableButtons();
    }

    //-------------------------------------------------------------------------------
    public void valueChanged(ListSelectionEvent e) {
        TreePath path = recorderTree.getSelectionPath();
        if (path == null || (disableRectree && editedRecorder == null)) {
            addButton.setEnabled(false);
            addAsButton.setEnabled(false);
        } else {
            addButton.setEnabled(recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));
            addAsButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().hasAddAsOption()
                    && recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));
        }
        removeScriptButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (recordableScripts.getSelectedIndices().length != 0
                        || nonRecordableScripts.getSelectedIndices().length != 0));
        editScriptButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && ((recordableTabbedPane.getSelectedIndex() == 2
                        && recordableScripts.getSelectedIndices().length != 0)
                        || (recordableTabbedPane.getSelectedIndex() == 3
                                && nonRecordableScripts.getSelectedIndices().length != 0)));
        testScriptButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (PlatformManager.getPlatform(PlatformSettings.getPlatformType()) instanceof ITestRunner)
                && recordableTabbedPane.getSelectedIndex() == 2
                && recordableScripts.getSelectedIndices().length != 0);
    }

    //-------------------------------------------------------------------------------
    public void valueChanged(TreeSelectionEvent e) {
        TreePath path = recorderTree.getSelectionPath();
        if (path == null || disableRectree) {
            editRecorderAction.setEnabled(false);
            removeAction.setEnabled(false);
            if (path == null) {
                addButton.setEnabled(false);
                addAsButton.setEnabled(false);
            }
        } else {
            removeAction.setEnabled(true);
            editRecorderAction.setEnabled(true);
            addButton.setEnabled(recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));
            addAsButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().hasAddAsOption()
                    && recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));

        }
    }

    //-------------------------------------------------------------------------------
    public void stateChanged(ChangeEvent e) {
        TreePath path = recorderTree.getSelectionPath();
        if (path == null || (disableRectree && editedRecorder == null)) {
            addButton.setEnabled(false);
            addAsButton.setEnabled(false);
        } else {
            addButton.setEnabled(recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));
            addAsButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().hasAddAsOption()
                    && recordableTabbedPane.getSelectedIndex() != 3
                    && (recordableVariables.getSelectedIndices().length != 0
                            || recordableMethods.getSelectedIndices().length != 0
                            || recordableScripts.getSelectedIndices().length != 0));
        }
        createScriptButton.setVisible(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (recordableTabbedPane.getSelectedIndex() == 2 || recordableTabbedPane.getSelectedIndex() == 3));
        removeScriptButton.setVisible(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (recordableTabbedPane.getSelectedIndex() == 2 || recordableTabbedPane.getSelectedIndex() == 3));
        editScriptButton.setVisible(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (recordableTabbedPane.getSelectedIndex() == 2 || recordableTabbedPane.getSelectedIndex() == 3));
        testScriptButton.setVisible(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (PlatformManager.getPlatform(PlatformSettings.getPlatformType()) instanceof ITestRunner)
                && recordableTabbedPane.getSelectedIndex() == 2);
        testScriptButton.setEnabled(PlatformSettings.getGUIControllerForPlatform().isScriptingSupport()
                && (PlatformManager.getPlatform(PlatformSettings.getPlatformType()) instanceof ITestRunner)
                && recordableTabbedPane.getSelectedIndex() == 2
                && recordableScripts.getSelectedIndices().length != 0);

    }

    //===============================================================================
    // GUI methods

    //-------------------------------------------------------------------------------
    private void layoutGUI() {

        stopPanel = FormsUtils.build("p ~ p:g p:g ~ p", "0011|" + "22__|" + "33__||" + "4556", iterationRadioButton,
                conditionRadioButton, "      (constant or appropriate", "        method or variable)", stopLabel,
                stopField, stopEEButton).getPanel();

        treePanel = FormsUtils.build("p:g ~ p ~ p ~ p ~ p:g", "00000 f:p:g||" + "_123_ p", recorderScr,
                newRecorderButton, editRecorderButton, removeButton).getPanel();

        generalPanel = FormsUtils.build("p ~ p:g ' p", "011||" + "234", " Name: ", nameField, " Output: ",
                outputField, outputBrowseButton).getPanel();

        whenPanel = FormsUtils.build("p ~ p:g ~ p ~ p", "0000|" + "1111|" + "234_|" + "5555|" + "6666|" + "7889|",
                new Separator("Record"), whenIterationButton, whenIterationIntervalButton,
                whenIterationIntervalField, wiiLabel, whenRunRadioButton, whenConditionRadioButton,
                whenConditionLabel, whenConditionField, whenEEButton).getPanel();

        recordableTabbedPane.addTab("Variables", recordableVariableScr);
        recordableTabbedPane.addTab("Methods", recordableMethodScr);
        recordableTabbedPane.addChangeListener(this);

        recordablePanel = FormsUtils.build("p:g ~ p ~ p ~ p",
                "_012 d||" + "3333 p||" + "4445|" + "4446||" + "4447||" + "4448||" + "4449||" + "444A||"
                        + "444_ f:p:g||" + "444B p",
                advancedButton, addRecorderButton, cancelButton, CellConstraints.RIGHT,
                new Separator("Select recording related elements"), recordableTabbedPane, " ", addButton,
                addAsButton, createScriptButton, editScriptButton, removeScriptButton, testScriptButton).getPanel();

        editPanel = FormsUtils.build("p:g", "0||" + "1||" + "2", generalPanel, whenPanel, recordablePanel)
                .getPanel();

        content = FormsUtils
                .build("~ f:p:g(0.4) f:p:g(0.6) ~", "01|" + "21 f:p:g|", stopPanel, treePanel, editPanel)
                .getPanel();

        this.setLayout(new BorderLayout());
        final JScrollPane sp = new JScrollPane(content);
        this.add(sp, BorderLayout.CENTER);
        this.setPreferredSize(new Dimension(579, 636));

        iterationRadioButton.setName("rbtn_wizard_record_stopiteration");
        conditionRadioButton.setName("rbtn_wizard_record_stopcondition");
        whenIterationButton.setName("rbtn_wizard_record_enditeration");
        whenIterationIntervalButton.setName("rbtn_wizard_record_afteriteration");
        whenRunRadioButton.setName("rbtn_wizard_record_endrun");
        whenConditionRadioButton.setName("rbtn_wizard_record_whencnd");
        stopField.setName("fld_wizard_record_stopfld");
        nameField.setName("fld_wizard_record_namefld");
        outputField.setName("fld_wizard_record_output");
        whenIterationIntervalField.setName("fld_wizard_record_afteriteration");
        whenConditionField.setName("fld_wizard_record_whencnd");
        stopEEButton.setName("btn_wizard_record_stopextended");
        whenEEButton.setName("btn_wizard_record_whenextended");
        outputBrowseButton.setName("btn_wizard_record_browseoutput");
        advancedButton.setName("btn_wizard_record_advanced");
        addRecorderButton.setName("btn_wizard_record_addrecorder");
        cancelButton.setName("btn_wizard_record_cancel");
        recordableTabbedPane.setName("pane_wizard_record_recordables");
        addButton.setName("btn_wizard_record_add");
        addAsButton.setName("btn_wizard_record_addas");
        createScriptButton.setName("btn_wizard_record_create");
        editScriptButton.setName("btn_wizard_record_edit");
        removeScriptButton.setName("btn_wizard_record_remove");
        recordableVariables.setName("lst_wizard_record_varlst");
        recordableMethods.setName("lst_wizard_record_methodlst");
        recordableScripts.setName("lst_wizard_record_datasrclst");
        nonRecordableScripts.setName("lst_wizard_record_misclst");
        recorderTree.setName("tree_wizard_record_recordertree");
        createScriptButton.setName("btn_wizard_record_createscript");
        removeScriptButton.setName("btn_wizard_record_removescript");
        editScriptButton.setName("btn_wizard_record_editscript");
        testScriptButton.setName("btn_wizard_record_testscript");
    }

    //-------------------------------------------------------------------------------
    private void initialize() {
        stopPanel.setBorder(BorderFactory.createTitledBorder("Stop each run"));
        stopPanel.setPreferredSize(new Dimension(300, 110));

        ai.aitia.meme.utils.GUIUtils.createButtonGroup(iterationRadioButton, conditionRadioButton);
        iterationRadioButton.setActionCommand("STOP_ITERATION");
        conditionRadioButton.setActionCommand("STOP_CONDITION");

        stopField.addCaretListener(this);
        stopEEButton.setEnabled(false);

        nameField.setPreferredSize(new Dimension(150, 20));
        outputField.setPreferredSize(new Dimension(130, 20));
        outputBrowseButton.setActionCommand("OUTPUT_BROWSE");
        outputBrowseButton.setPreferredSize(new Dimension(20, 20));
        outputBrowseButton.setToolTipText("Can be used only at local simulation running.");

        whenIterationButton.setActionCommand("WHEN_ITERATION");
        whenIterationIntervalButton.setActionCommand("WHEN_ITERATION_INTERVAL");
        whenRunRadioButton.setActionCommand("WHEN_RUN");
        whenConditionRadioButton.setActionCommand("WHEN_CONDITION");

        advancedButton.setActionCommand("ADVANCED");
        addRecorderButton.setActionCommand("ADD_OR_MODIFY");
        cancelButton.setActionCommand("CANCEL");

        recordableVariables.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        recordableMethods.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        recordableScripts.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        recordableScripts.setModel(new DefaultListModel());
        nonRecordableScripts.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        nonRecordableScripts.setModel(new DefaultListModel());

        treePanel.setBorder(BorderFactory.createTitledBorder("Recorders"));

        recorderScr.setPreferredSize(new Dimension(200, 400));
        recorderScr.setBorder(null);
        recorderTree.setRootVisible(false);
        recorderTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        recorderTree.setCellRenderer(new RecorderTreeRenderer());
        recorderTree.addTreeSelectionListener(this);

        recorderTree.addMouseListener(new MouseAdapter() {
            JPopupMenu contextMenu = null;

            @Override
            public void mouseClicked(MouseEvent e) {
                DefaultMutableTreeNode node = mouseOnNode(e);
                if (SwingUtilities.isLeftMouseButton(e) && e.getClickCount() == 2 && node != null
                        && !disableRectree)
                    hybridAction(null, editRecorderAction);
            }

            @Override
            public void mouseReleased(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e) && e.getComponent().isEnabled()) {
                    if (contextMenu == null) {
                        contextMenu = new JPopupMenu();
                        contextMenu.setName("cmenu_wizard_record_recorderscmenu");
                        contextMenu.add(newRecorderAction);
                        contextMenu.getComponent(contextMenu.getComponentCount() - 1).setName("btn_newrecorder");
                        contextMenu.add(editRecorderAction);
                        contextMenu.getComponent(contextMenu.getComponentCount() - 1).setName("btn_editrecorder");
                        contextMenu.addSeparator();
                        contextMenu.add(removeAction);
                        contextMenu.getComponent(contextMenu.getComponentCount() - 1).setName("btn_removerecorder");
                    }
                    DefaultMutableTreeNode node = mouseOnNode(e);
                    if (node != null)
                        recorderTree.setSelectionPath(new TreePath(node.getPath()));
                    else
                        recorderTree.setSelectionPath(recorderTree.getPathForRow(recorderTree.getRowCount() - 1));
                    editRecorderAction.setEnabled(!disableRectree);
                    removeAction.setEnabled(!disableRectree);
                    newRecorderAction.setEnabled(!disableRectree);
                    contextMenu.show(e.getComponent(), e.getX(), e.getY());
                }
            }

            private DefaultMutableTreeNode mouseOnNode(MouseEvent e) {
                Point loc = e.getPoint();
                TreePath path = recorderTree.getPathForLocation(20, loc.y);
                if (path != null)
                    return (DefaultMutableTreeNode) path.getLastPathComponent();
                return null;
            }
        });

        editPanel.setBorder(BorderFactory.createTitledBorder("Recorder settings"));

        nameField.addCaretListener(this);
        outputField.addCaretListener(this);

        ai.aitia.meme.utils.GUIUtils.createButtonGroup(whenRunRadioButton, whenIterationButton,
                whenIterationIntervalButton, whenConditionRadioButton);

        recordableTabbedPane.setPreferredSize(new Dimension(200, 260));
        recordableTabbedPane.setToolTipTextAt(0, "Recordable variables of the model.");
        recordableTabbedPane.setToolTipTextAt(1, "Recordable methods of the model.");

        recordableMethodScr.setBorder(null);
        recordableVariableScr.setBorder(null);
        recordableScriptScr.setBorder(null);
        nonRecordableScriptScr.setBorder(null);

        GUIUtils.ToggleClickMultiSelection ms = new GUIUtils.ToggleClickMultiSelection() {
            @Override
            public void mousePressed(java.awt.event.MouseEvent e) {
                if (recordableTabbedPane.getSelectedIndex() >= 2 && e.getClickCount() == 2)
                    editScriptButton.doClick(0);
            }
        };

        recordableVariables.addMouseListener(ms);
        recordableMethods.addMouseListener(ms);
        recordableScripts.addMouseListener(ms);
        nonRecordableScripts.addMouseListener(ms);
        recordableVariables.addListSelectionListener(this);
        recordableMethods.addListSelectionListener(this);
        recordableScripts.addListSelectionListener(this);
        nonRecordableScripts.addListSelectionListener(this);

        addButton.setActionCommand("ADD");
        addAsButton.setActionCommand("ADD_AS");
        createScriptButton.setActionCommand("CREATE");
        createScriptButton.setVisible(false);
        editScriptButton.setActionCommand("EDIT_SCRIPT");
        editScriptButton.setVisible(false);
        removeScriptButton.setActionCommand("REMOVE_SCRIPT");
        removeScriptButton.setVisible(false);
        testScriptButton.setActionCommand("TEST_SCRIPT");
        testScriptButton.setVisible(false);

        ai.aitia.meme.utils.GUIUtils.addActionListener(this, iterationRadioButton, conditionRadioButton,
                whenIterationButton, whenIterationIntervalButton, whenRunRadioButton, whenConditionRadioButton,
                addButton, addAsButton, addRecorderButton, cancelButton, advancedButton, createScriptButton,
                outputBrowseButton, removeScriptButton, editScriptButton, testScriptButton);
    }

    //----------------------------------------------------------------------------------------------------
    private void initializePageForPlatform() {
        IGUIController controller = PlatformSettings.getGUIControllerForPlatform();

        if (!controller.isStopFixIntervalEnabled() && !controller.isStopConditionEnabled())
            throw new IllegalStateException("Platform " + PlatformSettings.getPlatformType().toString()
                    + " does not support any type of " + "stopping condition.");

        if (!controller.isRecordEveryIterationEnabled() && !controller.isRecordNIterationEnabled()
                && !controller.isRecordEndOfTheRunsEnabled() && !controller.isRecordConditionEnabled())
            throw new IllegalStateException("Platform " + PlatformSettings.getPlatformType().toString()
                    + " does not support any type of " + "recording condition.");

        iterationRadioButton.setEnabled(controller.isStopFixIntervalEnabled());
        conditionRadioButton.setEnabled(controller.isStopConditionEnabled());

        if (controller.isStopFixIntervalEnabled() && !controller.isStopConditionEnabled()) {
            iterationRadioButton.setSelected(true);
            if (stopField.getText().trim().length() == 0)
                stopField.setText("100");
            setStopLabel("STOP_ITERATION");
        } else if (!controller.isStopFixIntervalEnabled() && controller.isStopConditionEnabled()) {
            conditionRadioButton.setSelected(true);
            setStopLabel("STOP_CONDITION");
        }

        if (controller.isRecordEveryIterationEnabled())
            whenIterationButton.setSelected(true);
        else if (controller.isRecordNIterationEnabled()) {
            whenIterationIntervalButton.setSelected(true);
            whenIterationIntervalField.setEnabled(true);
            wiiLabel.setEnabled(true);
        } else if (controller.isRecordEndOfTheRunsEnabled())
            whenRunRadioButton.setSelected(true);
        else {
            whenConditionRadioButton.setSelected(true);
            whenConditionLabel.setEnabled(true);
            whenConditionField.setEnabled(true);
            whenEEButton.setEnabled(true);
        }
    }

    //-------------------------------------------------------------------------------
    /** Resets the page. */
    private void resetPage() {
        IGUIController controller = PlatformSettings.getGUIControllerForPlatform();
        disableRectree = false;
        if (controller.isStopFixIntervalEnabled()) {
            iterationRadioButton.setSelected(true);
            stopField.setText("100");
        } else {
            conditionRadioButton.setSelected(true);
            stopField.setText("");
        }
        enableDisableSettings(false);
        addRecorderButton.setEnabled(false);
        resetSettings();
        root = new DefaultMutableTreeNode();
        recorderTree.setModel(new DefaultTreeModel(root));
        editRecorderAction.setEnabled(false);
        removeAction.setEnabled(false);
    }

    //-------------------------------------------------------------------------------
    /** Enables/disables the recorder editing components of the page according to the
     *  value of <code>enabled</code>.
     */
    private void enableDisableSettings(boolean enabled) {
        nameField.setEnabled(enabled);
        outputField.setEnabled(enabled);
        outputBrowseButton.setEnabled(enabled && ParameterSweepWizard.getPreferences().isLocalRun());
        whenIterationButton.setEnabled(
                enabled && PlatformSettings.getGUIControllerForPlatform().isRecordEveryIterationEnabled());
        whenIterationIntervalButton
                .setEnabled(enabled && PlatformSettings.getGUIControllerForPlatform().isRecordNIterationEnabled());
        wiiLabel.setEnabled(
                enabled && enabled && PlatformSettings.getGUIControllerForPlatform().isRecordNIterationEnabled());
        whenRunRadioButton.setEnabled(
                enabled && PlatformSettings.getGUIControllerForPlatform().isRecordEndOfTheRunsEnabled());
        whenConditionRadioButton
                .setEnabled(enabled && PlatformSettings.getGUIControllerForPlatform().isRecordConditionEnabled());
        whenConditionLabel
                .setEnabled(enabled && PlatformSettings.getGUIControllerForPlatform().isRecordConditionEnabled());
        advancedButton.setEnabled(enabled && isAdvancedButtonEnabled());
        cancelButton.setEnabled(enabled);
    }

    //-------------------------------------------------------------------------------
    /** Resets the recorder editing components. */
    private void resetSettings() {
        IGUIController controller = PlatformSettings.getGUIControllerForPlatform();
        nameField.setText("");
        outputField.setText("");
        outputField.setToolTipText("");
        outputBrowseButton.setEnabled(ParameterSweepWizard.getPreferences().isLocalRun());
        whenIterationIntervalField.setEnabled(false);
        whenIterationIntervalField.setText("");
        whenConditionField.setText("");
        whenConditionField.setEnabled(false);
        whenEEButton.setEnabled(false);
        if (controller.isRecordEveryIterationEnabled())
            whenIterationButton.setSelected(true);
        else if (controller.isRecordNIterationEnabled()) {
            whenIterationIntervalButton.setSelected(true);
            whenIterationIntervalField.setEnabled(true);
            wiiLabel.setEnabled(true);
        } else if (controller.isRecordEndOfTheRunsEnabled())
            whenRunRadioButton.setSelected(true);
        else {
            whenConditionRadioButton.setSelected(true);
            whenConditionLabel.setEnabled(true);
            whenConditionField.setEnabled(true);
            whenEEButton.setEnabled(true);
        }
        recordableTabbedPane.setSelectedIndex(0);
        recordableVariables.clearSelection();
        recordableMethods.clearSelection();
        recordableScripts.clearSelection();
        nonRecordableScripts.clearSelection();
        addButton.setEnabled(false);
        addAsButton.setEnabled(false);
        removeScriptButton.setEnabled(false);
        editScriptButton.setEnabled(false);
        testScriptButton.setEnabled(false);
        addRecorderButton.setText(" Add recorder ");
    }

    //-------------------------------------------------------------------------------
    /** Disables the recoders tree. */
    private void disableRecTree() {
        disableRectree = true;
        newRecorderAction.setEnabled(false);
        editRecorderAction.setEnabled(false);
        removeAction.setEnabled(false);
    }

    //===============================================================================
    // private methods

    //----------------------------------------------------------------------------------------------------
    private boolean isNewParameter(RecordableInfo ri) {
        if (PlatformSettings.getGUIControllerForPlatform().canNewParametersAlsoRecordables())
            return false; // we don't care
        List<ParameterInfo> newParameters = owner.getNewParameters_internal();
        if (!PlatformSettings.getGUIControllerForPlatform().isNewParametersEnabled() || newParameters == null
                || newParameters.size() == 0)
            return false;
        String name = null;
        if (InfoConverter.isVariable(ri))
            name = ri.getName();
        else if (ri.getAccessibleName().startsWith("get"))
            name = ri.getAccessibleName().substring(3, ri.getAccessibleName().length() - 2);
        if (name == null)
            return false;
        ParameterInfo dummy = new ParameterInfo(Util.capitalize(name), "", null);
        if (newParameters.contains(dummy))
            return true;
        dummy = new ParameterInfo(Util.uncapitalize(name), "", null);
        return newParameters.contains(dummy);
    }

    //----------------------------------------------------------------------------------------------------
    private boolean isAdvancedButtonEnabled() {
        IGUIController controller = PlatformSettings.getGUIControllerForPlatform();
        int count = 0;
        if (controller.isWriteEveryRecordingEnabled())
            count++;
        if (controller.isWriteNIterationEnabled())
            count++;
        if (controller.isWriteEndOfTheRunsEnabled())
            count++;
        return count > 1;
    }

    //-------------------------------------------------------------------------------
    /** Changes the label of the simulation stopping condition field. */
    private void setStopLabel(String command) {
        if (command.equals("STOP_ITERATION"))
            stopLabel.setText("       Value: ");
        else
            stopLabel.setText(" Condition: ");
    }

    //-------------------------------------------------------------------------------
    /** Returns whether the recorder name <code>name</code> is new or is already used
     *  by an other recorder.
     */
    private boolean isNewName(String name) {
        for (int i = 0; i < root.getChildCount(); ++i) {
            String recName = root.getChildAt(i).toString();
            if (name.equals(recName))
                return false;
        }
        return true;
    }

    //----------------------------------------------------------------------------------------------------
    private List<String> invalidRecorderPath() {
        List<String> result = new ArrayList<String>();
        if (ParameterSweepWizard.getPreferences().isLocalRun())
            return result;
        for (int i = 0; i < root.getChildCount(); ++i) {
            DefaultMutableTreeNode recorderNode = (DefaultMutableTreeNode) root.getChildAt(i);
            DefaultMutableTreeNode resultNode = (DefaultMutableTreeNode) recorderNode.getChildAt(0);
            ResultInfo ri = (ResultInfo) resultNode.getUserObject();
            if (ri.getFile().contains("\\") || ri.getFile().contains("/"))
                result.add(recorderNode.toString());
        }
        return result;
    }

    //-------------------------------------------------------------------------------
    /** Checks whether the simulation stopping condition is valid or not. */
    private boolean checkEndOfSimulation() {
        String candidate = new String(stopField.getText().trim());
        if ("".equals(candidate)) {
            Utilities.userAlert(owner, "Stop condition is empty.");
            return false;
        }
        if (iterationRadioButton.isSelected()) { // fix iteration
            try {
                double d = Double.parseDouble(candidate); // fix iteration
                if (d <= 0) {
                    Utilities.userAlert(owner, "Number of iterations must be greater than 0.");
                    return false;
                }
                return true;
            } catch (NumberFormatException e) {
                String error = null;
                // variable or method
                MemberInfo candidateInfo = new MemberInfo(candidate, "", null);
                RecordableElement candidateElement = new RecordableElement(candidateInfo);
                DefaultListModel listModel = (DefaultListModel) recordableVariables.getModel();
                int index = listModel.indexOf(candidateElement);
                if (index != -1) { // there is variable with the same name
                    MemberInfo mi = ((RecordableElement) listModel.get(index)).getInfo();
                    if (iterationTypeNames.contains(mi.getType()))
                        return true;
                    else
                        error = " is an inappropriate variable.";
                }

                if (PlatformSettings.getPlatformType() == PlatformType.NETLOGO
                        || PlatformSettings.getPlatformType() == PlatformType.NETLOGO5)
                    candidate += " ";
                else if (!candidate.endsWith("()"))
                    candidate += "()";
                candidateInfo.setName(candidate);
                listModel = (DefaultListModel) recordableMethods.getModel();
                index = listModel.indexOf(candidateElement);
                if (index != -1) { // there is a method with the same name
                    MemberInfo mi = ((RecordableElement) listModel.get(index)).getInfo();
                    if (iterationTypeNames.contains(mi.getType()))
                        return true;
                    else {
                        error = stopField.getText().trim().endsWith("()") ? "" : "()";
                        error += " is an inappropriate method.";
                    }
                }

                listModel = (DefaultListModel) recordableScripts.getModel();
                index = listModel.indexOf(candidateElement);
                if (index != -1) { // there is a script with the same name
                    MemberInfo mi = ((RecordableElement) listModel.get(index)).getInfo();
                    if (iterationTypeNames.contains(mi.getType()))
                        return true;
                    else {
                        error = stopField.getText().trim().endsWith("()") ? "" : "()";
                        error += " is an inappropriate method.";
                    }
                }

                if (error == null)
                    error = " is an unknown name or a wrong constant value.";
                Utilities.userAlert(owner, stopField.getText().trim() + error);
                return false;
            }
        } else { // condition
            String error = PlatformManager.getPlatform(PlatformSettings.getPlatformType()).checkCondition(candidate,
                    owner);
            if (error == null)
                return true;
            Utilities.userAlert(owner, error);
            return false;
        }
    }

    //------------------------------------------------------------------------------
    /** Creates a {@link ai.aitia.meme.paramsweep.gui.info.TimeInfo TimeInfo} object
     *  from the settings of the new recorder. A TimeInfo object represents the recording
     *  time or conditon (and the writing time) of a recorder.
     * @param error output parameter for the error message (if any)
     */
    private TimeInfo getTimeInfo(String[] error) {
        if (whenIterationButton.isSelected())
            return new TimeInfo(Mode.ITERATION);
        else if (whenRunRadioButton.isSelected())
            return new TimeInfo(Mode.RUN);
        else if (whenIterationIntervalButton.isSelected()) {
            String no = whenIterationIntervalField.getText().trim();
            if (no.length() == 0) {
                error[0] = "The selected recording time option is incomplete: empty field.";
                return null;
            }
            try {
                long l = Long.parseLong(no);
                if (l < 1)
                    throw new NumberFormatException();
            } catch (NumberFormatException e) {
                error[0] = "The selected recording time option is wrong: the contain of the field is not a positive integer.";
                return null;
            }
            return new TimeInfo(Mode.ITERATION_INTERVAL, no);
        } else {
            String condition = whenConditionField.getText().trim();
            String condError = PlatformManager.getPlatform(PlatformSettings.getPlatformType())
                    .checkCondition(condition, owner);
            if (condError != null) {
                error[0] = condError;
                return null;
            }
            return new TimeInfo(Mode.CONDITION, condition);
        }
    }

    //------------------------------------------------------------------------------
    /** Sets the writing mode (which is set in the Advanced dialog) in a 
     *  {@link ai.aitia.meme.paramsweep.gui.info.TimeInfo TimeInfo} object.
     */
    private void applyAdvancedSettings(TimeInfo info) {
        if (advancedDialog == null)
            return;
        long[] arg = new long[1];
        WriteMode mode = advancedDialog.getWriteType(arg);
        mode = postCheck(info, mode, arg[0]);
        info.setWriteMode(mode, arg[0]);
    }

    //------------------------------------------------------------------------------
    /** Checks whether the writing time <code>orig</code> and the recording time (or
     *  condition) in <code>info</code> are compatible or not. If not the writing time
     *  is changed.
     * @param arg the argument of the writing time (if any)
     * @return the original or the modified writing time
     */
    private WriteMode postCheck(TimeInfo info, WriteMode orig, long arg) {
        boolean mustChange = false;
        switch (info.getType()) {
        case RUN:
        case CONDITION:
            mustChange = (orig == WriteMode.ITERATION_INTERVAL);
            break;
        case ITERATION_INTERVAL:
            mustChange = (arg > 0 && arg < Long.parseLong(info.getArg()));
        }
        return (mustChange ? WriteMode.RECORD : orig);
    }

    //-------------------------------------------------------------------------------
    /** Checks whether the output file specified by <code>file</code> is already
     *  assigned to an other recorder or not.
     */
    private boolean isAlreadyAssigned(String file) {
        if (root.getChildCount() == 0)
            return false;
        for (int i = 0; i < root.getChildCount(); ++i) {
            DefaultMutableTreeNode recorder = (DefaultMutableTreeNode) root.getChildAt(i);
            if (recorder.equals(editedRecorder))
                continue;
            DefaultMutableTreeNode resNode = (DefaultMutableTreeNode) recorder.getFirstChild();
            ResultInfo ri = (ResultInfo) resNode.getUserObject();
            if (file.trim().equals(ri.getFile().trim()))
                return true;
        }
        return false;
    }

    //----------------------------------------------------------------------------------------------------
    private boolean isValidRecorderFile(final String file) {
        for (final char c : file.toCharArray()) {
            if (!Character.isLetterOrDigit(c) && c != '_' && c != '.')
                return false;
        }
        return true;
    }

    //-------------------------------------------------------------------------------
    /** Returns whether the recorder belongs to <code>node</code> contains the
     *  recodable element <code>info</code> as a child or not.
     */
    private boolean contains(DefaultMutableTreeNode node, MemberInfo info) {
        for (int i = 2; i < node.getChildCount(); ++i) {
            DefaultMutableTreeNode child = (DefaultMutableTreeNode) node.getChildAt(i);
            RecordableElement re = (RecordableElement) child.getUserObject();
            if (info.equals(re.getInfo()))
                return true;
        }
        return false;
    }

    //-------------------------------------------------------------------------------
    /** Generates a unique recorder name. */
    private String generateName() {
        List<Integer> nums = new ArrayList<Integer>();
        for (int i = 0; i < root.getChildCount(); ++i) {
            String candidate = root.getChildAt(i).toString().toLowerCase();
            if (candidate.startsWith("recorder")) {
                String num = candidate.substring(8);
                try {
                    nums.add(new Integer(num));
                } catch (NumberFormatException e) {
                }
            }
        }
        Collections.sort(nums);
        return nums.size() == 0 ? "Recorder0" : "Recorder" + String.valueOf((nums.get(nums.size() - 1) + 1));
    }

    //-------------------------------------------------------------------------------
    /** Edits the recorder that belongs to <code>node</code> so this method fills the appropriate
     *  components with the informations of the recorder.
     */
    private void edit(DefaultMutableTreeNode recorder) {
        editedRecorder = recorder;
        nameField.setText(recorder.toString());
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) recorder.getFirstChild();
        ResultInfo ri = (ResultInfo) node.getUserObject();
        outputField.setText(ri.getFile());
        outputField.setToolTipText(ri.getFile());
        node = (DefaultMutableTreeNode) recorder.getChildAt(1);
        TimeInfo ti = (TimeInfo) node.getUserObject();
        switch (ti.getType()) {
        case ITERATION:
            whenIterationButton.doClick();
            break;
        case ITERATION_INTERVAL:
            whenIterationIntervalButton.doClick();
            whenIterationIntervalField.setText(ti.getArg());
            break;
        case RUN:
            whenRunRadioButton.doClick();
            break;
        case CONDITION:
            whenConditionRadioButton.doClick();
            whenConditionField.setText(ti.getArg());
        }
        nameField.grabFocus();
        addRecorderButton.setEnabled(true);
        advancedDialog = new WriteToFileDialog(ParameterSweepWizard.getFrame(), ti.getWriteType(),
                ti.getWriteArg());
    }

    //------------------------------------------------------------------------------
    /** Notifies all listeners of the recorder tree that the nodes of the tree are
     *  changed.
     */
    private void fireTreeNodesChanged(TreePath path) {
        // Guaranteed to return a non-null array
        Object[] listeners = ((DefaultTreeModel) recorderTree.getModel()).getListeners(TreeModelListener.class);
        TreeModelEvent e = null;
        // Process the listeners last to first, notifying
        // those that are interested in this event
        for (int i = listeners.length - 1; i >= 0; i--) {
            // Lazily create the event:
            if (e == null)
                e = new TreeModelEvent(this, path);
            ((TreeModelListener) listeners[i]).treeNodesChanged(e);
        }
    }

    //------------------------------------------------------------------------------
    /** Creates the recorder tree from an XML document.
     * @param recordersElement the node of the XML document that belongs to the recorders
     * @throws WizardLoadingException if the XML document is invalid
     */
    private void createRecorderTree(Element recordersElement) throws WizardLoadingException {
        // clear tree
        root = new DefaultMutableTreeNode();
        DefaultTreeModel treeModel = new DefaultTreeModel(root);
        recorderTree.setModel(treeModel);

        NodeList nodes = recordersElement.getElementsByTagName(WizardSettingsManager.RECORDER);
        if (nodes == null)
            return;
        for (int i = 0; i < nodes.getLength(); ++i) {
            Element recorder = (Element) nodes.item(i);
            String name = recorder.getAttribute(WizardSettingsManager.NAME);
            if (name.equals(""))
                throw new WizardLoadingException(true,
                        "missing 'name' attribute at node: " + WizardSettingsManager.RECORDER);
            DefaultMutableTreeNode recorderTreeNode = new DefaultMutableTreeNode(name);
            treeModel.insertNodeInto(recorderTreeNode, root, i);

            NodeList nl = recorder.getElementsByTagName(WizardSettingsManager.OUTPUT_FILE);
            if (nl == null || nl.getLength() == 0)
                throw new WizardLoadingException(true, "missing node: " + WizardSettingsManager.OUTPUT_FILE);
            NodeList content = nl.item(0).getChildNodes();
            if (content == null || content.getLength() == 0)
                throw new WizardLoadingException(true,
                        "missing content at node: " + WizardSettingsManager.OUTPUT_FILE);
            String output = ((Text) content.item(0)).getNodeValue();
            ResultInfo ri = new ResultInfo(output.trim());
            DefaultMutableTreeNode outputNode = new DefaultMutableTreeNode(ri);
            treeModel.insertNodeInto(outputNode, recorderTreeNode, 0);

            nl = null;
            nl = recorder.getElementsByTagName(WizardSettingsManager.TIME);
            if (nl == null || nl.getLength() == 0)
                throw new WizardLoadingException(true, "missing node: " + WizardSettingsManager.TIME);
            Element timeElement = (Element) nl.item(0);
            String type = timeElement.getAttribute(WizardSettingsManager.TYPE);
            if (type == null || type.equals(""))
                throw new WizardLoadingException(true,
                        "missing 'type' attribute at node: " + WizardSettingsManager.TIME);
            TimeInfo ti = null;
            try {
                if (type.equals("ITERATION") || type.equals("RUN"))
                    ti = new TimeInfo(type);
                else {
                    content = null;
                    content = timeElement.getChildNodes();
                    if (content == null || content.getLength() == 0)
                        throw new WizardLoadingException(true,
                                "missing content at node: " + WizardSettingsManager.TIME);
                    String arg = ((Text) content.item(0)).getNodeValue();
                    ti = new TimeInfo(type, arg);
                }
            } catch (IllegalArgumentException e) {
                throw new WizardLoadingException(true,
                        "invalid 'type' attribute at node: " + WizardSettingsManager.TIME);
            }

            nl = null;
            nl = recorder.getElementsByTagName(WizardSettingsManager.WRITING_TIME);
            if (nl == null || nl.getLength() == 0)
                throw new WizardLoadingException(true, "missing node: " + WizardSettingsManager.WRITING_TIME);
            Element writingElement = (Element) nl.item(0);
            type = null;
            type = writingElement.getAttribute(WizardSettingsManager.TYPE);
            if (type == null || type.equals(""))
                throw new WizardLoadingException(true,
                        "missing 'type' attribute at node: " + WizardSettingsManager.WRITING_TIME);
            long arg = -1;
            if (type.equals("ITERATION_INTERVAL")) {
                content = null;
                content = writingElement.getChildNodes();
                if (content == null || content.getLength() == 0)
                    throw new WizardLoadingException(true,
                            "missing content at node: " + WizardSettingsManager.WRITING_TIME);
                try {
                    arg = Long.parseLong(((Text) content.item(0)).getNodeValue());
                } catch (NumberFormatException e) {
                    throw new WizardLoadingException(true,
                            "invalid content at node: " + WizardSettingsManager.WRITING_TIME);
                }
            }
            try {
                ti.setWriteMode(type, arg);
            } catch (IllegalArgumentException e) {
                throw new WizardLoadingException(true,
                        "invalid content at node: " + WizardSettingsManager.WRITING_TIME);
            }

            DefaultMutableTreeNode timeNode = new DefaultMutableTreeNode(ti);
            treeModel.insertNodeInto(timeNode, recorderTreeNode, 1);

            nl = null;
            nl = recorder.getElementsByTagName(WizardSettingsManager.MEMBER);
            if (nl == null)
                continue;
            InfoConverter.loadRecorderElementsFromXml(owner.getClassLoader(), treeModel, recorderTreeNode, nl,
                    scriptSupport);
            recorderTree.expandPath(new TreePath(recorderTreeNode.getPath()));
        }

        if (root.getChildCount() > 0) {
            recorderTree.setSelectionPath(new TreePath(((DefaultMutableTreeNode) root.getChildAt(0)).getPath()));
        }
    }

    //----------------------------------------------------------------------------------------------------
    private List<String> emptyRecorders() {
        List<String> result = new ArrayList<String>();
        for (int i = 0; i < root.getChildCount(); ++i) {
            DefaultMutableTreeNode recorder = (DefaultMutableTreeNode) root.getChildAt(i);
            if (recorder.getChildCount() <= 2)
                result.add(recorder.toString());
        }
        return result;
    }

    //===============================================================================
    // nested classes

    //----------------------------------------------------------------------------------------------------
    /** This class renders the recorder tree. */
    @SuppressWarnings("serial")
    private class RecorderTreeRenderer extends DefaultTreeCellRenderer {

        //====================================================================================================
        // members

        private ImageIcon recorderIcon = ExpandedEditor.getIcon("recorder.png");
        private ImageIcon timeIcon = ExpandedEditor.getIcon("time.png");
        private ImageIcon conditionIcon = ExpandedEditor.getIcon("warning.png");
        private ImageIcon variableIcon = ExpandedEditor.getIcon("model_version.png");
        private ImageIcon methodIcon = ExpandedEditor.getIcon("model_name.png");

        //====================================================================================================
        // methods

        //----------------------------------------------------------------------------------------------------
        @Override
        public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded,
                boolean leaf, int row, boolean hasFocus) {
            Component com = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
            Object userObject = node.getUserObject();
            Icon icon = null;
            if (userObject instanceof String) {
                icon = recorderIcon;
                String name = (String) userObject;
                if (node.getChildCount() <= 2)
                    ((JLabel) com).setText("<html><font color=grey><i>" + name + "</i></font></html>");
            } else if (userObject instanceof TimeInfo) {
                TimeInfo ti = (TimeInfo) userObject;
                if (ti.getType() == Mode.CONDITION)
                    icon = conditionIcon;
                else
                    icon = timeIcon;
            } else if (userObject instanceof RecordableElement) {
                RecordableElement re = (RecordableElement) userObject;
                if (re.getInfo().getName().endsWith(")"))
                    icon = methodIcon;
                else
                    icon = variableIcon;
            }
            if (icon != null)
                ((JLabel) com).setIcon(icon);
            tree.setToolTipText(value.toString());
            ((JLabel) com).setToolTipText(value.toString());
            return com;
        }
    }
}