0001: /*
0002: * <copyright>
0003: *
0004: * Copyright 2000-2004 BBNT Solutions, LLC
0005: * under sponsorship of the Defense Advanced Research Projects
0006: * Agency (DARPA).
0007: *
0008: * You can redistribute this software and/or modify it under the
0009: * terms of the Cougaar Open Source License as published on the
0010: * Cougaar Open Source Website (www.cougaar.org).
0011: *
0012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
0013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
0014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
0015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
0016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
0022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0023: *
0024: * </copyright>
0025: */
0026:
0027: package org.cougaar.tools.csmart.ui.viewer;
0028:
0029: import org.cougaar.tools.csmart.core.db.CommunityDbUtils;
0030: import org.cougaar.tools.csmart.core.db.DBConflictHandler;
0031: import org.cougaar.tools.csmart.core.db.DBUtils;
0032: import org.cougaar.tools.csmart.core.db.ExperimentDB;
0033: import org.cougaar.tools.csmart.core.db.PDbBase;
0034: import org.cougaar.tools.csmart.core.db.PopulateDb;
0035: import org.cougaar.tools.csmart.core.property.ModifiableComponent;
0036: import org.cougaar.tools.csmart.core.property.ModificationEvent;
0037: import org.cougaar.tools.csmart.core.property.ModificationListener;
0038: import org.cougaar.tools.csmart.experiment.DBExperiment;
0039: import org.cougaar.tools.csmart.experiment.Experiment;
0040: import org.cougaar.tools.csmart.recipe.RecipeComponent;
0041: import org.cougaar.tools.csmart.recipe.RecipeList;
0042: import org.cougaar.tools.csmart.society.SocietyComponent;
0043: import org.cougaar.tools.csmart.society.ui.SocietyUIComponent;
0044: import org.cougaar.tools.csmart.ui.util.Util;
0045: import org.cougaar.util.log.Logger;
0046:
0047: import javax.swing.*;
0048: import javax.swing.event.AncestorEvent;
0049: import javax.swing.event.AncestorListener;
0050: import javax.swing.event.TreeModelEvent;
0051: import javax.swing.event.TreeModelListener;
0052: import javax.swing.event.TreeSelectionEvent;
0053: import javax.swing.event.TreeSelectionListener;
0054: import javax.swing.filechooser.FileFilter;
0055: import javax.swing.tree.DefaultMutableTreeNode;
0056: import javax.swing.tree.DefaultTreeModel;
0057: import javax.swing.tree.TreePath;
0058: import java.awt.*;
0059: import java.awt.event.ActionEvent;
0060: import java.awt.event.ActionListener;
0061: import java.awt.event.MouseEvent;
0062: import java.io.File;
0063: import java.io.IOException;
0064: import java.io.ObjectInputStream;
0065: import java.util.ArrayList;
0066: import java.util.Collection;
0067: import java.util.Collections;
0068: import java.util.Enumeration;
0069: import java.util.EventObject;
0070: import java.util.Map;
0071: import java.util.Set;
0072: import java.util.Vector;
0073:
0074: /**
0075: * The Organizer holds all the component a user creates
0076: * and manipulates in CSMART
0077: * @property org.cougaar.tools.csmart.doWorkspace if false do NOT read / write from the
0078: * workspace file, nor set listeners to support doing so.
0079: */
0080: public class Organizer extends JScrollPane {
0081: private static final String DEFAULT_FILE_NAME = "Default Workspace.xml";
0082: private static final String FRAME_TITLE = "CSMART Launcher";
0083: private static final long UPDATE_DELAY = 5000L;
0084: private boolean updateNeeded = false;
0085: private long nextUpdate = 0L;
0086:
0087: // If you set this give property to false, then no Workspace file is
0088: // read or written
0089: private static boolean doWorkspace = true;
0090: static {
0091: String s = System
0092: .getProperty("org.cougaar.tools.csmart.doWorkspace");
0093: if ("false".equalsIgnoreCase(s)) {
0094: doWorkspace = false;
0095: }
0096: }
0097:
0098: private String workspaceFileName;
0099: protected CSMART csmart;
0100: protected DefaultMutableTreeNode root;
0101: private Object lockObject = new Object();
0102: protected DefaultTreeModel model;
0103: protected OrganizerTree workspace;
0104: private Organizer organizer;
0105: private CMTDialog cmtDialog;
0106: private transient Logger log;
0107:
0108: private DBConflictHandler saveToDbConflictHandler = GUIUtils
0109: .createSaveToDbConflictHandler(this );
0110:
0111: // The stand-alone recipes that can be created in CSMART
0112: private Object[] recipeNameClassItems = null;
0113:
0114: // Define Unique Name sets
0115: private OrganizerNameSet experimentNames = new OrganizerNameSet(
0116: "Experiment", "Experiment", "isExperimentNameInDatabase");
0117: private OrganizerNameSet societyNames = new OrganizerNameSet(
0118: "Society", "Society", "isSocietyNameInDatabase");
0119: private OrganizerNameSet recipeNames = new OrganizerNameSet(
0120: "Recipe", "Recipe", "isRecipeNameInDatabase");
0121: private OrganizerNameSet folderNames = new OrganizerNameSet(
0122: "Folder", "Folder", null);
0123:
0124: // define helper class
0125: protected OrganizerHelper helper;
0126:
0127: private ArrayList recipes = new ArrayList();
0128:
0129: // Define actions for use on menus
0130:
0131: protected Action newExperimentFromDBAction = new AbstractAction(
0132: ActionUtil.NEW_EXPERIMENT_FROM_DB_ACTION) {
0133: public void actionPerformed(ActionEvent e) {
0134: organizer.selectExperimentFromDatabase();
0135: }
0136: };
0137:
0138: protected Action newExperimentFromFileAction = new AbstractAction(
0139: ActionUtil.NEW_EXPERIMENT_FROM_FILE_ACTION) {
0140: public void actionPerformed(ActionEvent e) {
0141: organizer.createExperimentFromFile();
0142: }
0143: };
0144:
0145: protected Action newExperimentFromUIAction = new AbstractAction(
0146: ActionUtil.NEW_EXPERIMENT_FROM_UI_ACTION) {
0147: public void actionPerformed(ActionEvent e) {
0148: organizer.createExperimentFromUI();
0149: }
0150: };
0151:
0152: protected Action[] newExperimentActions = {
0153: newExperimentFromDBAction, newExperimentFromFileAction,
0154: newExperimentFromUIAction };
0155:
0156: protected Action newFolderAction = new AbstractAction(
0157: ActionUtil.NEW_FOLDER_ACTION) {
0158: public void actionPerformed(ActionEvent e) {
0159: organizer.newFolder();
0160: }
0161: };
0162:
0163: protected Action renameWorkspaceAction = new AbstractAction(
0164: ActionUtil.RENAME_ACTION) {
0165: public void actionPerformed(ActionEvent e) {
0166: organizer.renameWorkspace();
0167: }
0168: };
0169:
0170: protected AbstractAction deleteExperimentFromDatabaseAction = new AbstractAction(
0171: ActionUtil.DELETE_EXPERIMENT_FROM_DATABASE_ACTION) {
0172: public void actionPerformed(ActionEvent e) {
0173: organizer.deleteExperimentFromDatabase();
0174: }
0175: };
0176:
0177: protected AbstractAction deleteRecipeFromDatabaseAction = new AbstractAction(
0178: ActionUtil.DELETE_RECIPE_FROM_DATABASE_ACTION) {
0179: public void actionPerformed(ActionEvent e) {
0180: organizer.deleteRecipeFromDatabase();
0181: }
0182: };
0183:
0184: protected AbstractAction buildExperimentAction = new AbstractAction(
0185: ActionUtil.BUILD_ACTION, new ImageIcon(getClass()
0186: .getResource("Experiment16t.gif"))) {
0187: public void actionPerformed(ActionEvent e) {
0188: organizer.startExperimentBuilder();
0189: }
0190: };
0191:
0192: protected AbstractAction runExperimentAction = new AbstractAction(
0193: ActionUtil.RUN_ACTION, new ImageIcon(getClass()
0194: .getResource("EC16.gif"))) {
0195: public void actionPerformed(ActionEvent e) {
0196: organizer.startConsole();
0197: }
0198: };
0199:
0200: protected AbstractAction duplicateAction = new AbstractAction(
0201: ActionUtil.DUPLICATE_ACTION) {
0202: public void actionPerformed(ActionEvent e) {
0203: organizer.duplicate();
0204: }
0205: };
0206:
0207: protected AbstractAction deleteExperimentAction = new AbstractAction(
0208: ActionUtil.DELETE_ACTION) {
0209: public void actionPerformed(ActionEvent e) {
0210: organizer.deleteExperiment();
0211: }
0212: };
0213:
0214: protected AbstractAction deleteAction = new AbstractAction(
0215: ActionUtil.DELETE_ACTION) {
0216: public void actionPerformed(ActionEvent e) {
0217: organizer.delete();
0218: }
0219: };
0220:
0221: protected AbstractAction renameExperimentAction = new AbstractAction(
0222: ActionUtil.RENAME_ACTION) {
0223: public void actionPerformed(ActionEvent e) {
0224: organizer.renameExperiment();
0225: }
0226: };
0227:
0228: protected AbstractAction renameAction = new AbstractAction(
0229: ActionUtil.RENAME_ACTION) {
0230: public void actionPerformed(ActionEvent e) {
0231: organizer.rename();
0232: }
0233: };
0234:
0235: protected AbstractAction configureAction = new AbstractAction(
0236: ActionUtil.CONFIGURE_ACTION, new ImageIcon(getClass()
0237: .getResource("SB16.gif"))) {
0238: public void actionPerformed(ActionEvent e) {
0239: organizer.startBuilder();
0240: }
0241: };
0242:
0243: protected AbstractAction deleteRecipeAction = new AbstractAction(
0244: ActionUtil.DELETE_ACTION) {
0245: public void actionPerformed(ActionEvent e) {
0246: organizer.deleteRecipe();
0247: }
0248: };
0249:
0250: protected AbstractAction renameRecipeAction = new AbstractAction(
0251: ActionUtil.RENAME_ACTION) {
0252: public void actionPerformed(ActionEvent e) {
0253: organizer.renameRecipe();
0254: }
0255: };
0256:
0257: protected AbstractAction deleteFolderAction = new AbstractAction(
0258: ActionUtil.DELETE_ACTION) {
0259: public void actionPerformed(ActionEvent e) {
0260: organizer.deleteFolder();
0261: }
0262: };
0263:
0264: protected AbstractAction renameFolderAction = new AbstractAction(
0265: ActionUtil.RENAME_ACTION) {
0266: public void actionPerformed(ActionEvent e) {
0267: organizer.renameFolder();
0268: }
0269: };
0270:
0271: protected AbstractAction deleteSocietyAction = new AbstractAction(
0272: ActionUtil.DELETE_ACTION) {
0273: public void actionPerformed(ActionEvent e) {
0274: organizer.deleteSociety();
0275: }
0276: };
0277:
0278: protected AbstractAction renameSocietyAction = new AbstractAction(
0279: ActionUtil.RENAME_ACTION) {
0280: public void actionPerformed(ActionEvent e) {
0281: organizer.renameSociety();
0282: }
0283: };
0284:
0285: protected AbstractAction newRecipeFromDatabaseAction = new AbstractAction(
0286: "From Database") {
0287: public void actionPerformed(ActionEvent e) {
0288: organizer.selectRecipeFromDatabase();
0289: }
0290: };
0291:
0292: protected AbstractAction newRecipeBuiltInAction = new AbstractAction(
0293: "From Template") {
0294: public void actionPerformed(ActionEvent e) {
0295: organizer.newRecipe();
0296: }
0297: };
0298:
0299: protected Action[] newRecipeActions = {
0300: newRecipeFromDatabaseAction, newRecipeBuiltInAction };
0301:
0302: protected AbstractAction saveAction = new AbstractAction("Save") {
0303: public void actionPerformed(ActionEvent e) {
0304: organizer.saveInDatabase();
0305: }
0306: };
0307:
0308: /**
0309: * Construct a workspace, i.e. an user interface for a tree
0310: * of components (experiments, societies, recipes).
0311: * Reads the previous workspace from "Default Workspace.xml"
0312: * @param csmart The <code>CSMART</code> user interface containing this.
0313: */
0314: public Organizer(CSMART csmart) {
0315: this (csmart, null);
0316: }
0317:
0318: /**
0319: * Construct a workspace, i.e. an user interface for a tree
0320: * of components (experiments, societies, recipes).
0321: * If the workspace file name parameter is null,
0322: * reads the previous workspace from "Default Workspace.xml"
0323: * @param csmart The <code>CSMART</code> user interface containing this.
0324: * @param workspaceFileName file from which to read previous workspace
0325: */
0326: public Organizer(CSMART csmart, String workspaceFileName) {
0327: createLogger();
0328: organizer = this ;
0329: helper = new OrganizerHelper(this );
0330: initRecipes();
0331:
0332: setPreferredSize(new Dimension(400, 400));
0333: JPanel panel = new JPanel(new BorderLayout());
0334: setViewportView(panel);
0335: this .csmart = csmart;
0336: if (workspaceFileName == null) {
0337: this .workspaceFileName = DEFAULT_FILE_NAME;
0338: } else {
0339: this .workspaceFileName = workspaceFileName;
0340: }
0341:
0342: restore(this .workspaceFileName);
0343:
0344: if (!doWorkspace) {
0345: if (log.isInfoEnabled()) {
0346: log.info("Not using workspace file");
0347: }
0348: }
0349:
0350: if (root == null) {
0351: root = new DefaultMutableTreeNode(null, true);
0352: model = new DefaultTreeModel(root);
0353: }
0354: model.setAsksAllowsChildren(true);
0355:
0356: // This listener is only used to call update
0357: if (doWorkspace)
0358: model.addTreeModelListener(myModelListener);
0359:
0360: // When restoring workspace file, this should have been done already
0361: if (workspace == null)
0362: workspace = new OrganizerTree(model);
0363:
0364: DefaultCellEditor myEditor = new DefaultCellEditor(
0365: new JTextField()) {
0366: public boolean isCellEditable(EventObject e) {
0367: if (super .isCellEditable(e) && e instanceof MouseEvent) {
0368: TreePath path = workspace.getPathForLocation(
0369: ((MouseEvent) e).getX(), ((MouseEvent) e)
0370: .getY());
0371: if (path == null)
0372: return false;
0373: Object o = path.getLastPathComponent();
0374: Object userObject = ((DefaultMutableTreeNode) o)
0375: .getUserObject();
0376: if (userObject instanceof ModifiableComponent) {
0377: // don't allow edits which change the name,
0378: // use Rename menu action instead
0379: return false;
0380: } else
0381: return true; // renaming workspace or folder is always ok
0382: }
0383: return false;
0384: }
0385: };
0386: workspace.setCellEditor(myEditor);
0387: workspace.setCellRenderer(new OrganizerTreeCellRenderer(this ));
0388: workspace.setExpandsSelectedPaths(true);
0389: workspace.addTreeSelectionListener(mySelectionListener);
0390: workspace.addAncestorListener(myAncestorListener);
0391: workspace.setSelection(root);
0392: workspace.addMouseListener(new OrganizerMouseListener(this ,
0393: workspace));
0394: expandTree(); // fully expand workspace tree
0395: panel.add(workspace);
0396: setViewportView(panel);
0397: if (doWorkspace)
0398: updater.start();
0399: }
0400:
0401: private void createLogger() {
0402: log = CSMART.createLogger(this .getClass().getName());
0403: }
0404:
0405: private void initRecipes() {
0406: recipeNameClassItems = new Object[RecipeList.getRecipeCount()];
0407: for (int i = 0; i < RecipeList.getRecipeCount(); i++) {
0408: recipeNameClassItems[i] = new NameClassItem(RecipeList
0409: .getRecipeName(i), RecipeList.getRecipeClass(i));
0410: }
0411: }
0412:
0413: /**
0414: * Get the society component with the given name from the workspace;
0415: * this ensures that there is exactly one object per component
0416: * in the workspace.
0417: */
0418:
0419: public SocietyComponent getSociety(String name) {
0420: return (SocietyComponent) societyNames.getObjectInTree(
0421: SocietyComponent.class, root, name);
0422: }
0423:
0424: public RecipeComponent getRecipe(String name) {
0425: return (RecipeComponent) recipeNames.getObjectInTree(
0426: RecipeComponent.class, root, name);
0427: }
0428:
0429: ///////////////////////////////////////
0430: // Methods to get the user's selection
0431: ///////////////////////////////////////
0432:
0433: protected DefaultMutableTreeNode getSelectedNode() {
0434: TreePath selPath = workspace.getSelectionPath();
0435: if (selPath == null)
0436: return null;
0437: return (DefaultMutableTreeNode) selPath.getLastPathComponent();
0438: }
0439:
0440: ////////////////////////////////////
0441: // Start tools; used to start tools from menus
0442: ////////////////////////////////////
0443:
0444: protected void startBuilder() {
0445: csmart.runBuilder((ModifiableComponent) getSelectedNode()
0446: .getUserObject(), false);
0447: }
0448:
0449: private DBExperiment createExperiment(SocietyComponent society) {
0450: String name = generateExperimentName("Experiment for "
0451: + society.getSocietyName(), false);
0452: if (name == null)
0453: return null;
0454: return new DBExperiment(name, society, new RecipeComponent[0]);
0455: }
0456:
0457: private DBExperiment createExperiment(RecipeComponent recipe) {
0458: String name = generateExperimentName("Experiment for "
0459: + recipe.getRecipeName(), false);
0460: if (name == null)
0461: return null;
0462: return new DBExperiment(name, null,
0463: new RecipeComponent[] { recipe });
0464: }
0465:
0466: /**
0467: * When called from Organizer, the selected user object is an experiment.
0468: * When called from CSMART File-Build menu, the
0469: * selected user object is a recipe or society.
0470: */
0471: protected void startExperimentBuilder() {
0472: DBExperiment experiment = null;
0473: DefaultMutableTreeNode node = getSelectedNode();
0474: Object o = node.getUserObject();
0475: if (o instanceof SocietyComponent) {
0476: experiment = createExperiment((SocietyComponent) o);
0477: if (experiment != null)
0478: addExperimentAndComponentsToWorkspace(experiment,
0479: (DefaultMutableTreeNode) node.getParent());
0480: } else if (o instanceof RecipeComponent) {
0481: experiment = createExperiment((RecipeComponent) o);
0482: if (experiment != null)
0483: addExperimentAndComponentsToWorkspace(experiment,
0484: (DefaultMutableTreeNode) node.getParent());
0485: } else if (o instanceof Experiment)
0486: experiment = (DBExperiment) o;
0487: if (experiment != null)
0488: csmart.runExperimentBuilder(experiment, false);
0489: }
0490:
0491: /**
0492: * Try to run the console. This is called in three cases:
0493: * 1) the user has selected a runnable experiment
0494: * 2) the user has selected a society and this method creates an experiment
0495: * 3) the user has not selected an experiment and plans to attach to running nodes
0496: */
0497: protected void startConsole() {
0498: DefaultMutableTreeNode node = getSelectedNode();
0499: if (node == null) {
0500: csmart.runConsole(null);
0501: return;
0502: }
0503: Object o = node.getUserObject();
0504: DBExperiment experiment = null;
0505: if (o instanceof SocietyComponent) {
0506: experiment = createExperiment((SocietyComponent) o);
0507: if (experiment == null)
0508: return;
0509: addExperimentAndComponentsToWorkspace(experiment,
0510: (DefaultMutableTreeNode) node.getParent());
0511: if (!experiment.hasConfiguration())
0512: experiment.createDefaultConfiguration();
0513: // TODO: this generates an error -- null trial id in PdbBase.put
0514: experiment.save(saveToDbConflictHandler);
0515: } else if (o instanceof Experiment) {
0516: experiment = (DBExperiment) o;
0517: }
0518: // Start up the console on the experiment or with no experiment
0519: csmart.runConsole(experiment);
0520: }
0521:
0522: ///////////////////////////////////
0523: // Unique name support
0524: //////////////////////////////////
0525:
0526: /**
0527: * Get a unique name, optionally allowing the existing name.
0528: * Prompts the user for a name based on the original name,
0529: * and then checks the user entered value for uniqueness in
0530: * the CSMART workspace and in the database.
0531: */
0532:
0533: public String getUniqueExperimentName(String originalName,
0534: boolean allowExistingName) {
0535: return experimentNames.getUniqueName(originalName,
0536: allowExistingName);
0537: }
0538:
0539: protected String getUniqueSocietyName(String originalName,
0540: boolean allowExistingName) {
0541: return societyNames.getUniqueName(originalName,
0542: allowExistingName);
0543: }
0544:
0545: protected String getUniqueRecipeName(String originalName,
0546: boolean allowExistingName) {
0547: return recipeNames.getUniqueName(originalName,
0548: allowExistingName);
0549: }
0550:
0551: private String getUniqueFolderName(String originalName,
0552: boolean allowExistingName) {
0553: return folderNames.getUniqueName(originalName,
0554: allowExistingName);
0555: }
0556:
0557: protected boolean isUniqueSocietyName(String name) {
0558: return societyNames.isUniqueName(name);
0559: }
0560:
0561: /**
0562: * Generate unique names. Only prompts user if a generated
0563: * name conflicts in the database.
0564: */
0565:
0566: protected String generateExperimentName(String name,
0567: boolean allowExistingName) {
0568: return experimentNames.generateUniqueName(name,
0569: allowExistingName);
0570: }
0571:
0572: protected String generateSocietyName(String name,
0573: boolean allowExistingName) {
0574: return societyNames.generateUniqueName(name, allowExistingName);
0575: }
0576:
0577: protected String generateRecipeName(String name,
0578: boolean allowExistingName) {
0579: return recipeNames.generateUniqueName(name, allowExistingName);
0580: }
0581:
0582: /**
0583: * Generate an unique name with no user input.
0584: */
0585:
0586: protected String generateExperimentName(String name) {
0587: return experimentNames.generateName(name);
0588: }
0589:
0590: protected String generateSocietyName(String name) {
0591: return societyNames.generateName(name);
0592: }
0593:
0594: ///////////////////////////////////
0595: // New Experiments
0596: //////////////////////////////////
0597:
0598: protected void createExperimentFromFile() {
0599: SocietyComponent society = helper.createSocietyFromFile();
0600: if (society == null)
0601: return;
0602: DBExperiment experiment = createExperiment(society);
0603:
0604: // Add in community info for this society
0605: JFileChooser chooser = new JFileChooser(System
0606: .getProperty("org.cougaar.install.path"));
0607: chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
0608: chooser
0609: .setDialogTitle("Select the communities.xml file for this Society, if any");
0610: // Allow selection of XML files and directories
0611: chooser.addChoosableFileFilter(new FileFilter() {
0612: public boolean accept(File f) {
0613: if (f == null)
0614: return false;
0615: if (f.isDirectory())
0616: return true;
0617: if (!f.canRead())
0618: return false;
0619: return f.getName().endsWith(".xml");
0620: }
0621:
0622: public String getDescription() {
0623: return "XML Files";
0624: }
0625:
0626: });
0627:
0628: File xmlFile = null;
0629: while (xmlFile == null) {
0630: int result = chooser.showDialog(organizer, "OK");
0631: if (result != JFileChooser.APPROVE_OPTION)
0632: break;
0633: xmlFile = chooser.getSelectedFile();
0634: if (xmlFile != null) {
0635: if (!xmlFile.canRead())
0636: xmlFile = null;
0637: }
0638: }
0639:
0640: if (xmlFile != null
0641: && !CommunityDbUtils.importCommunityXML(xmlFile,
0642: experiment.getCommAsbID())) {
0643: // There may have been none, so don't complain too loudly.
0644: if (log.isInfoEnabled()) {
0645: log
0646: .info("crtExpFromFile got no Community XML data out of "
0647: + xmlFile.getAbsolutePath());
0648: }
0649: } else if (log.isDebugEnabled()) {
0650: log.debug("crtExpFromFile had xmlFile: " + xmlFile
0651: + " and or return from CommDbUtils not false");
0652: }
0653:
0654: DefaultMutableTreeNode experimentNode = addExperimentToWorkspace(
0655: experiment, getSelectedNode());
0656: addSocietyToWorkspace(society, experimentNode);
0657: }
0658:
0659: /**
0660: * Create an experiment and a society that will be specified
0661: * from the user interface.
0662: */
0663: protected void createExperimentFromUI() {
0664: String name = getUniqueExperimentName("", false);
0665: if (name == null)
0666: return;
0667: SocietyComponent society = new SocietyUIComponent(
0668: "Society for " + name);
0669: DBExperiment experiment = new DBExperiment(name, society, null);
0670: DefaultMutableTreeNode experimentNode = addExperimentToWorkspace(
0671: experiment, getSelectedNode());
0672: addSocietyToWorkspace(society, experimentNode);
0673: }
0674:
0675: ///////////////////////////////////
0676: // New Societies
0677: //////////////////////////////////
0678:
0679: protected void addSociety(SocietyComponent sc) {
0680: // add society as sibling of experiment
0681: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) (getSelectedNode()
0682: .getParent());
0683: addSocietyToWorkspace(sc, parentNode);
0684: }
0685:
0686: /////////////////////////////////
0687: // New Recipes
0688: /////////////////////////////////
0689:
0690: /**
0691: * Ensure that recipe name is unique in both CSMART and the database.
0692: * Optionally allow re-use of existing name.
0693: */
0694: protected void newRecipe() {
0695: Object[] values = recipeNameClassItems;
0696: Object answer = JOptionPane.showInputDialog(this ,
0697: "Select Recipe Type", "Select Recipe",
0698: JOptionPane.QUESTION_MESSAGE, null, values,
0699: "Empty Recipe");
0700: if (answer instanceof NameClassItem) {
0701: NameClassItem item = (NameClassItem) answer;
0702: // String name = generateRecipeName(item.name, false);
0703: String name = recipeNames.getUniqueName(item.name, false);
0704: if (name == null)
0705: return;
0706: RecipeComponent recipe = helper
0707: .createRecipe(name, item.cls);
0708: if (recipe != null) {
0709: recipe.saveToDatabase();
0710: addRecipeToWorkspace(recipe, getSelectedNode());
0711: }
0712: }
0713: } // end of newRecipe
0714:
0715: protected void newFolder() {
0716: String name = getUniqueFolderName("", false);
0717: if (name == null)
0718: return;
0719: addFolderToWorkspace(name, getSelectedNode());
0720: }
0721:
0722: /////////////////////////////////////
0723: // Rename items
0724: /////////////////////////////////////
0725:
0726: /**
0727: * Rename a folder, experiment, society or recipe.
0728: */
0729: protected void rename() {
0730: DefaultMutableTreeNode node = getSelectedNode();
0731: if (node == null)
0732: return;
0733: Object o = node.getUserObject();
0734: if (o == null)
0735: return;
0736: if (node.isRoot())
0737: renameWorkspace();
0738: else if (o instanceof Experiment)
0739: renameExperiment();
0740: else if (o instanceof SocietyComponent)
0741: renameSociety();
0742: else if (o instanceof RecipeComponent)
0743: renameRecipe();
0744: else if (o instanceof String)
0745: renameFolder();
0746: }
0747:
0748: /**
0749: * Rename the top-level workspace.
0750: */
0751:
0752: protected void renameWorkspace() {
0753: String name = JOptionPane.showInputDialog("New workspace name");
0754: if (name == null || name.equals(""))
0755: return;
0756: workspaceFileName = name;
0757: String rootName = this .workspaceFileName;
0758: int extpos = this .workspaceFileName.lastIndexOf('.');
0759: if (extpos >= 0) {
0760: rootName = rootName.substring(0, extpos);
0761: }
0762: root.setUserObject(rootName);
0763: model.nodeChanged(root);
0764: if (doWorkspace)
0765: update();
0766: }
0767:
0768: /**
0769: * Rename an experiment.
0770: */
0771:
0772: protected void renameExperiment() {
0773: DefaultMutableTreeNode node = getSelectedNode();
0774: final DBExperiment experiment = (DBExperiment) node
0775: .getUserObject();
0776: String originalName = experiment.getExperimentName();
0777: String newName = getUniqueExperimentName(originalName, true);
0778: if (newName == null)
0779: return;
0780: if (newName.equals(originalName))
0781: return;
0782: experimentNames.remove(originalName);
0783: experiment.setName(newName);
0784: if (experiment.isModified()) {
0785: GUIUtils.timeConsumingTaskStart(organizer);
0786: try {
0787: new Thread("SaveExperiment") {
0788: public void run() {
0789: experiment.save(saveToDbConflictHandler); // save under new name
0790: GUIUtils.timeConsumingTaskEnd(organizer);
0791: }
0792: }.start();
0793: } catch (RuntimeException re) {
0794: if (log.isErrorEnabled()) {
0795: log
0796: .error(
0797: "Runtime exception saving experiment",
0798: re);
0799: }
0800: GUIUtils.timeConsumingTaskEnd(organizer);
0801: }
0802: } // end of block to see if expt was modified.
0803: experimentNames.add(newName);
0804: model.nodeChanged(node);
0805: }
0806:
0807: /**
0808: * Rename a society.
0809: * If the society is in an experiment,
0810: * copy the society, rename it, and save it to the database.
0811: * If it's not in an experiment, then warn the user that they
0812: * need to save any experiments that contain it.
0813: */
0814: protected void renameSociety() {
0815: DefaultMutableTreeNode node = getSelectedNode();
0816: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node
0817: .getParent();
0818: // int index = parentNode.getIndex(node);
0819: SocietyComponent society = (SocietyComponent) node
0820: .getUserObject();
0821: String originalName = society.getSocietyName();
0822: String newName = getUniqueSocietyName(originalName, true);
0823: if (newName == null)
0824: return;
0825: if (newName.equals(originalName))
0826: return;
0827: societyNames.remove(originalName);
0828: Object o = parentNode.getUserObject();
0829: if (o instanceof Experiment) {
0830: Experiment experiment = (Experiment) o;
0831: model.removeNodeFromParent(node);
0832: experiment.removeSocietyComponent();
0833: SocietyComponent societyCopy = (SocietyComponent) society
0834: .copyAndSave(newName);
0835: experiment.addSocietyComponent(societyCopy);
0836:
0837: addSocietyToWorkspace(societyCopy, parentNode);
0838: } else {
0839: societyNames.add(newName);
0840: // Make this method in SocietyBase do the DB update
0841: // so we dont have to do a separate save
0842: society.setName(newName);
0843: //society.saveToDatabase();
0844: Enumeration nodes = root.depthFirstEnumeration();
0845: while (nodes.hasMoreElements()) {
0846: node = (DefaultMutableTreeNode) nodes.nextElement();
0847: if (node.getUserObject().equals(society))
0848: model.nodeChanged(node);
0849: }
0850: displayExperiments(society);
0851: }
0852: }
0853:
0854: /**
0855: * Rename a recipe.
0856: * If the recipe is in an experiment,
0857: * copy the recipe, rename it, and save it to the database.
0858: * If it's not in an experiment, then warn the user that they
0859: * need to save any experiments that contain it.
0860: */
0861: protected void renameRecipe() {
0862: DefaultMutableTreeNode node = getSelectedNode();
0863: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node
0864: .getParent();
0865: // int index = parentNode.getIndex(node);
0866: final RecipeComponent recipe = (RecipeComponent) node
0867: .getUserObject();
0868: String originalName = recipe.getRecipeName();
0869: String newName = getUniqueRecipeName(originalName, true);
0870: if (newName == null)
0871: return;
0872: if (newName.equals(originalName))
0873: return;
0874:
0875: Object o = parentNode.getUserObject();
0876:
0877: // If its local, create a copy with the new name
0878: if (o instanceof Experiment) {
0879: Experiment experiment = (Experiment) o;
0880: model.removeNodeFromParent(node);
0881: experiment.removeRecipeComponent(recipe);
0882: RecipeComponent recipeCopy = (RecipeComponent) recipe
0883: .copy(newName);
0884: experiment.addRecipeComponent(recipeCopy);
0885: recipeCopy.saveToDatabase();
0886: addRecipeToWorkspace(recipeCopy, parentNode);
0887:
0888: // If there are no other Nodes in the model with the originalName
0889: // then and only then we want to remove that name from the list
0890: // of recipeNames
0891: boolean nameStillUsed = false;
0892: Enumeration nodes = root.depthFirstEnumeration();
0893: while (nodes.hasMoreElements()) {
0894: node = (DefaultMutableTreeNode) nodes.nextElement();
0895: if (node.getUserObject().equals(recipe)) {
0896: // Original recipe still in the model someplace
0897: // so don't remove the name
0898: nameStillUsed = true;
0899: break;
0900: }
0901: }
0902: if (!nameStillUsed)
0903: recipeNames.remove(originalName);
0904: } else {
0905: // Only really want to remove
0906: // the original name if there are no other nodes with this name
0907: // If its global (as in this block) we're going to change
0908: // the recipe name throughout so this is correct
0909: recipeNames.remove(originalName);
0910:
0911: recipeNames.add(newName);
0912:
0913: // This method now updates the DB as well
0914: recipe.setName(newName);
0915:
0916: // So only save it to the DB if necessary
0917: // Note however that
0918: // since the property names may have changed,
0919: // it may be I always want to re-save
0920: if (recipe.isModified())
0921: recipe.saveToDatabase();
0922:
0923: Enumeration nodes = root.depthFirstEnumeration();
0924: while (nodes.hasMoreElements()) {
0925: node = (DefaultMutableTreeNode) nodes.nextElement();
0926: if (node.getUserObject().equals(recipe))
0927: model.nodeChanged(node);
0928: }
0929:
0930: // Now that changing a recipe name
0931: // doesn't change the recipe ID, there is no need to
0932: // resave all these experiments
0933: // when you change a recipe name
0934: // displayExperiments(recipe);
0935: } // end of block on global change
0936: }
0937:
0938: /**
0939: * Rename a folder.
0940: */
0941:
0942: protected void renameFolder() {
0943: String originalName = (String) (getSelectedNode()
0944: .getUserObject());
0945: String newName = getUniqueFolderName(originalName, true);
0946: if (newName == null)
0947: return;
0948: DefaultMutableTreeNode node = getSelectedNode();
0949: if (node.getUserObject().equals(newName))
0950: return;
0951: node.setUserObject(newName);
0952: model.nodeChanged(node);
0953: folderNames.remove(originalName);
0954: folderNames.add(newName);
0955: }
0956:
0957: ///////////////////////////////////
0958: // Select items from database
0959: ///////////////////////////////////
0960:
0961: /**
0962: * Prompt the user for an experiment to load from the database.
0963: * Return an array with 2 string: experiment name and ID.
0964: * Note that the return is null if the user cancels the operation in any way.
0965: **/
0966: protected String[] selectExperimentFromDBToLoad() {
0967: Map experimentNamesMap = ExperimentDB.getExperimentNames();
0968: Set keys = experimentNamesMap.keySet();
0969: JComboBox cb = new JComboBox(keys.toArray(new String[keys
0970: .size()]));
0971: cb.setEditable(false);
0972: JPanel panel = new JPanel();
0973: panel.add(new JLabel("Select Experiment:"));
0974: panel.add(cb);
0975: int result = JOptionPane.showConfirmDialog(null, panel,
0976: "Experiment", JOptionPane.OK_CANCEL_OPTION,
0977: JOptionPane.PLAIN_MESSAGE);
0978: if (result != JOptionPane.OK_OPTION)
0979: return null;
0980: String experimentName = (String) cb.getSelectedItem();
0981: // if the CSMART workspace contains an experiment with this name,
0982: // then force the user to select a new unique name
0983: if (experimentNames.contains(experimentName)) {
0984: experimentName = generateExperimentName(experimentName,
0985: false);
0986: if (experimentName == null)
0987: return null;
0988: }
0989:
0990: String[] res = new String[2];
0991: res[0] = experimentName;
0992: res[1] = (String) experimentNamesMap.get(experimentName);
0993: return res;
0994: }
0995:
0996: /**
0997: * Prompt the user for an experiment to load, then create it from the database,
0998: * and put it in the workspace.
0999: **/
1000: protected void selectExperimentFromDatabase() {
1001: String[] res = selectExperimentFromDBToLoad();
1002: if (res == null)
1003: return;
1004: selectGivenExperimentFromDatabase(res[0], res[1]);
1005: }
1006:
1007: /**
1008: * Take the given experiment name and ID, and use them to try to load
1009: * an experiment from the database and add them to the workspace.
1010: **/
1011: protected void selectGivenExperimentFromDatabase(
1012: String experimentName, String experimentId) {
1013: selectGivenExperimentFromDatabase(experimentName, experimentId,
1014: true);
1015: }
1016:
1017: /**
1018: * Take the given experiment name and ID, and use them to try to load
1019: * an experiment from the database and add them to the workspace.
1020: * However, the final argument indicates whether to actually display
1021: * the CMTDialog window. If not, then whatever threads were already
1022: * selected will be used to load the experiment.
1023: **/
1024: protected void selectGivenExperimentFromDatabase(
1025: String experimentName, String experimentId,
1026: boolean withPrompt) {
1027: boolean haveCMTAssembly = false;
1028: final String originalExperimentName = experimentName;
1029: // Does this experiment use a CMT assembly for configuration?
1030: // If so, use the CMTDialog
1031: // Otherwise, just do the load (which is now done through the CMTDialog)
1032: // Note: CMTDialog creates a CMTSociety -- should it be creating
1033: // a SocietyDBComponent?
1034:
1035: if (log.isDebugEnabled()) {
1036: log.debug("selectExperimentFromDB: Experiment Id: "
1037: + experimentId + " name: " + originalExperimentName
1038: + " will now get name " + experimentName);
1039: }
1040:
1041: // Does the experiment have a CMT configuration assembly
1042: if (DBUtils.containsCMTAssembly(experimentId)) {
1043: haveCMTAssembly = true;
1044: // get threads and groups information
1045: cmtDialog = new CMTDialog(csmart, this , experimentName,
1046: experimentId, !withPrompt);
1047: while (!cmtDialog.isULThreadSelected()
1048: && !cmtDialog.wasCancelled()) {
1049: JOptionPane
1050: .showMessageDialog(this ,
1051: "You must select at least one thread.",
1052: "No Thread Selected",
1053: JOptionPane.ERROR_MESSAGE);
1054: cmtDialog = new CMTDialog(csmart, this , experimentName,
1055: experimentId);
1056: }
1057: if (cmtDialog.wasCancelled())
1058: return;
1059: }
1060:
1061: if (haveCMTAssembly) {
1062: if (log.isDebugEnabled()) {
1063: log
1064: .debug("selectExptFromDB about to do cmtDialog.processResults");
1065: }
1066: GUIUtils.timeConsumingTaskStart(organizer);
1067: try {
1068: new Thread("SelectExperiment") {
1069: public void run() {
1070: // a potentially long process
1071: if (cmtDialog.processResults()) {
1072: final String trialId = cmtDialog
1073: .getTrialId();
1074: if (log.isDebugEnabled()) {
1075: log
1076: .debug("selectExptFromDB after cmtDialog.processResults with new name/id: "
1077: + cmtDialog
1078: .getExperimentName()
1079: + "/"
1080: + cmtDialog
1081: .getExperimentId()
1082: + " trialID: "
1083: + trialId
1084: + " and orig name: "
1085: + originalExperimentName);
1086: }
1087: if (trialId != null) {
1088: DBExperiment experiment = helper
1089: .createExperiment(
1090: originalExperimentName,
1091: cmtDialog
1092: .getExperimentName(),
1093: cmtDialog
1094: .getExperimentId(),
1095: trialId);
1096: if (experiment != null)
1097: addExperimentAndComponentsToWorkspace(
1098: experiment,
1099: getSelectedNode());
1100: }
1101: }
1102:
1103: GUIUtils.timeConsumingTaskEnd(organizer);
1104: }
1105: }.start();
1106: } catch (RuntimeException re) {
1107: if (log.isErrorEnabled()) {
1108: log.error("Runtime exception creating experiment",
1109: re);
1110: }
1111: GUIUtils.timeConsumingTaskEnd(organizer);
1112: }
1113: } else {
1114: final String trialId = ExperimentDB
1115: .getTrialId(experimentId);
1116: final String expName = experimentName;
1117: final String expId = experimentId;
1118: if (log.isDebugEnabled()) {
1119: log
1120: .debug("selectExptFromDB got nonCMT expt to load. About to call helper to do load with origName: "
1121: + originalExperimentName
1122: + ", new name: "
1123: + expName
1124: + ", new ID: "
1125: + expId
1126: + ", and trialID: " + trialId);
1127: }
1128: if (trialId != null) {
1129: GUIUtils.timeConsumingTaskStart(organizer);
1130: try {
1131: new Thread("SelectExperiment") {
1132: public void run() {
1133: // a potentially long process
1134: DBExperiment experiment = helper
1135: .createExperiment(
1136: originalExperimentName,
1137: expName, expId, trialId);
1138: if (experiment != null)
1139: addExperimentAndComponentsToWorkspace(
1140: experiment, getSelectedNode());
1141: GUIUtils.timeConsumingTaskEnd(organizer);
1142: }
1143: }.start();
1144: } catch (RuntimeException re) {
1145: if (log.isErrorEnabled()) {
1146: log
1147: .error(
1148: "Runtime exception creating experiment",
1149: re);
1150: }
1151: GUIUtils.timeConsumingTaskEnd(organizer);
1152: }
1153: } // end of if block
1154: } // end of else block
1155: } // end of selectExpFromDB
1156:
1157: // return true if component is in workspace
1158: // and is not the child of an experiment
1159: protected boolean isInWorkspace(ModifiableComponent mc) {
1160: Enumeration nodes = root.depthFirstEnumeration();
1161: while (nodes.hasMoreElements()) {
1162: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
1163: .nextElement();
1164: if (node.getUserObject().equals(mc)) {
1165: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node
1166: .getParent();
1167: Object parentObject = parentNode.getUserObject();
1168: if (parentObject != null
1169: && !(parentObject instanceof Experiment))
1170: return true;
1171: }
1172: }
1173: return false;
1174: }
1175:
1176: // TODO: determine how to handle naming of components that
1177: // are children of the experiment and also at the "top level"
1178: // these names are currently added twice and not deleted
1179: // when the experiment is deleted
1180: protected void addExperimentAndComponentsToWorkspace(
1181: DBExperiment experiment, DefaultMutableTreeNode node) {
1182: DefaultMutableTreeNode expNode = addExperimentToWorkspace(
1183: experiment, node);
1184: SocietyComponent societyComponent = experiment
1185: .getSocietyComponent();
1186:
1187: if (!isInWorkspace(societyComponent))
1188: addSocietyToWorkspace(societyComponent, node);
1189: RecipeComponent[] recipes = experiment.getRecipeComponents();
1190: for (int i = 0; i < recipes.length; i++)
1191: if (!isInWorkspace(recipes[i]))
1192: addRecipeToWorkspace(recipes[i], node);
1193: // add non-editable societies and recipes
1194: // as children of experiment
1195: addSocietyToWorkspace(societyComponent, expNode);
1196: for (int i = 0; i < recipes.length; i++)
1197: addRecipeToWorkspace(recipes[i], expNode);
1198: // select new experiment in workspace
1199: workspace.setSelection(expNode);
1200: }
1201:
1202: protected void selectRecipeFromDatabase() {
1203: Map recipeNamesHT = helper.getRecipeNamesFromDatabase();
1204: Set dbRecipeNames = recipeNamesHT.keySet();
1205: if (dbRecipeNames.isEmpty()) {
1206: JOptionPane.showMessageDialog(this ,
1207: "There are no recipes in the database",
1208: "No Database Recipes",
1209: JOptionPane.INFORMATION_MESSAGE);
1210: return;
1211: }
1212: Object[] selectedRecipes = Util.getObjectsFromList(this ,
1213: new ArrayList(dbRecipeNames), "Recipes",
1214: "Select Recipes");
1215: if (selectedRecipes == null)
1216: return;
1217: // where to put the new recipes
1218: DefaultMutableTreeNode parentNode = getSelectedNode();
1219: if (parentNode == null)
1220: parentNode = root;
1221: for (int i = 0; i < selectedRecipes.length; i++) {
1222: String recipeName = (String) selectedRecipes[i];
1223: String recipeId = (String) recipeNamesHT.get(recipeName);
1224: if (recipeNames.contains(recipeName))
1225: recipeName = recipeNames.getUniqueName(recipeName,
1226: false);
1227: if (recipeName == null)
1228: return;
1229: RecipeComponent rc = helper.getDatabaseRecipe(recipeId,
1230: recipeName);
1231: if (rc != null)
1232: addRecipeToWorkspace(rc, parentNode);
1233: }
1234: }
1235:
1236: ////////////////////////////////////
1237: // Copy items
1238: ////////////////////////////////////
1239:
1240: /**
1241: * Copy an experiment, society or recipe.
1242: */
1243:
1244: protected void duplicate() {
1245: DefaultMutableTreeNode node = getSelectedNode();
1246: if (node == null)
1247: return;
1248: Object o = node.getUserObject();
1249: if (o == null)
1250: return;
1251: if (o instanceof Experiment)
1252: copyExperiment((Experiment) o, true);
1253: else if (o instanceof SocietyComponent)
1254: copySociety((SocietyComponent) o);
1255: else if (o instanceof RecipeComponent)
1256: copyRecipe((RecipeComponent) o);
1257: }
1258:
1259: protected Experiment copyExperiment(Experiment experiment,
1260: boolean save) {
1261: String newName = generateExperimentName(experiment
1262: .getExperimentName(), false);
1263: if (newName == null)
1264: return null;
1265: final DBExperiment experimentCopy = (DBExperiment) experiment
1266: .copy(newName);
1267: if (save) {
1268: // save copy in database
1269: GUIUtils.timeConsumingTaskStart(organizer);
1270: try {
1271: new Thread("DuplicateExperiment") {
1272: public void run() {
1273: experimentCopy.save(saveToDbConflictHandler);
1274: GUIUtils.timeConsumingTaskEnd(organizer);
1275: }
1276: }.start();
1277: } catch (RuntimeException re) {
1278: if (log.isErrorEnabled()) {
1279: log.error(
1280: "Runtime exception duplicating experiment",
1281: re);
1282: }
1283: GUIUtils.timeConsumingTaskEnd(organizer);
1284: }
1285: }
1286: DefaultMutableTreeNode node = (DefaultMutableTreeNode) findNode(
1287: experiment).getParent();
1288: if (node == null) {
1289: if (log.isErrorEnabled()) {
1290: log.error("Experiment not found in workspace: "
1291: + experiment.getExperimentName());
1292: }
1293: }
1294: // add copy as sibling of original
1295: addExperimentAndComponentsToWorkspace(experimentCopy, node);
1296: return experimentCopy;
1297: }
1298:
1299: /**
1300: * Copy society; save the society to the database;
1301: * put the society in the workspace.
1302: * If the society is in an experiment, put the society in the workspace
1303: * as a sibling of the experiment, else put it in the workspace
1304: * as a sibling of the society.
1305: * @param society the society to copy
1306: * @return SocietyComponent the copied society
1307: */
1308: protected SocietyComponent copySociety(SocietyComponent society) {
1309: String newName = generateSocietyName(society.getSocietyName(),
1310: false);
1311: if (newName == null)
1312: return null;
1313: final SocietyComponent societyCopy = (SocietyComponent) society
1314: .copy(newName);
1315: // save society copy
1316: GUIUtils.timeConsumingTaskStart(organizer);
1317: try {
1318: new Thread("DuplicateSociety") {
1319: public void run() {
1320: // Check the return value?
1321: societyCopy.saveToDatabase();
1322: GUIUtils.timeConsumingTaskEnd(organizer);
1323: }
1324: }.start();
1325: } catch (RuntimeException re) {
1326: if (log.isErrorEnabled()) {
1327: log.error("Runtime exception duplicating society", re);
1328: }
1329: GUIUtils.timeConsumingTaskEnd(organizer);
1330: }
1331: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) getSelectedNode()
1332: .getParent();
1333: if (isSelectedNodeInExperiment())
1334: addSocietyToWorkspace(societyCopy,
1335: (DefaultMutableTreeNode) parentNode.getParent());
1336: else
1337: addSocietyToWorkspace(societyCopy, parentNode);
1338: return societyCopy;
1339: }
1340:
1341: /**
1342: * Copy the recipe component; generate a new unique name for the copy;
1343: * add the copy to the Workspace as a sibling of the original.
1344: * If the recipe is in an experiment, put the recipe in the workspace
1345: * as a sibling of the experiment, else put it in the workspace
1346: * as a sibling of the recipe.
1347: * @param recipe the recipe to copy
1348: * @return RecipeComponent the copied recipe
1349: */
1350: protected RecipeComponent copyRecipe(RecipeComponent recipe) {
1351: String newName = generateRecipeName(recipe.getRecipeName(),
1352: false);
1353: if (newName == null)
1354: return null;
1355: final RecipeComponent recipeCopy = (RecipeComponent) recipe
1356: .copy(newName);
1357:
1358: // If we got an error copying the recipe, don't try to save it
1359: if (recipeCopy == null)
1360: return null;
1361:
1362: GUIUtils.timeConsumingTaskStart(organizer);
1363: try {
1364: new Thread("DuplicateRecipe") {
1365: public void run() {
1366: recipeCopy.saveToDatabase();
1367: GUIUtils.timeConsumingTaskEnd(organizer);
1368: }
1369: }.start();
1370: } catch (RuntimeException re) {
1371: if (log.isErrorEnabled()) {
1372: log.error("Runtime exception duplicating recipe", re);
1373: }
1374: GUIUtils.timeConsumingTaskEnd(organizer);
1375: }
1376: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) getSelectedNode()
1377: .getParent();
1378: if (isSelectedNodeInExperiment())
1379: addRecipeToWorkspace(recipeCopy,
1380: (DefaultMutableTreeNode) parentNode.getParent());
1381: else
1382: addRecipeToWorkspace(recipeCopy, parentNode);
1383: return recipeCopy;
1384: }
1385:
1386: ////////////////////////////////////
1387: // Delete items
1388: ////////////////////////////////////
1389:
1390: protected void delete() {
1391: DefaultMutableTreeNode node = getSelectedNode();
1392: if (node == null)
1393: return;
1394: Object o = node.getUserObject();
1395: if (o == null)
1396: return;
1397: if (o instanceof Experiment)
1398: deleteExperiment();
1399: else if (o instanceof SocietyComponent)
1400: deleteSociety();
1401: else if (o instanceof RecipeComponent)
1402: deleteRecipe();
1403: }
1404:
1405: protected void deleteExperiment() {
1406: deleteExperimentInNode(getSelectedNode());
1407: }
1408:
1409: protected void deleteExperimentInNode(DefaultMutableTreeNode node) {
1410: if (node == null)
1411: return;
1412: DBExperiment experiment = (DBExperiment) node.getUserObject();
1413: if (!experiment.isEditable()) {
1414: int result = JOptionPane.showConfirmDialog(this ,
1415: "Experiment " + experiment.getExperimentName()
1416: + " is not editable; delete anyway?",
1417: "Experiment Not Editable",
1418: JOptionPane.YES_NO_OPTION,
1419: JOptionPane.WARNING_MESSAGE);
1420: if (result != JOptionPane.YES_OPTION)
1421: return;
1422: }
1423: model.removeNodeFromParent(node);
1424: String experimentName = experiment.getExperimentName();
1425: experimentNames.remove(experimentName);
1426: SocietyComponent society = experiment.getSocietyComponent();
1427: if (society != null)
1428: societyNames.remove(society.getSocietyName());
1429: int n = experiment.getRecipeComponentCount();
1430: for (int i = 0; i < n; i++)
1431: recipeNames.remove(experiment.getRecipeComponent(i)
1432: .getRecipeName());
1433: // ask if experiment should be deleted from database
1434: int result = JOptionPane.showConfirmDialog(this ,
1435: "Delete experiment " + experiment.getExperimentName()
1436: + " from database?",
1437: "Delete Experiment From Database",
1438: JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
1439: if (result != JOptionPane.YES_OPTION)
1440: return;
1441: ExperimentDB.deleteExperiment(experiment.getExperimentID(),
1442: experimentName);
1443: }
1444:
1445: /**
1446: * Note that this simply deletes the society from the workspace;
1447: * if the society was included in an experiment, then the experiment
1448: * still retains the society.
1449: */
1450: protected void deleteSociety() {
1451: deleteSocietyInNode(getSelectedNode());
1452: }
1453:
1454: protected void deleteSocietyInNode(
1455: DefaultMutableTreeNode societyNode) {
1456: SocietyComponent society = (SocietyComponent) societyNode
1457: .getUserObject();
1458: model.removeNodeFromParent(societyNode);
1459: // if deleted last reference to society in the workspace
1460: // ask if society should be deleted from database and delete it
1461: if (findNodeNamed(society.getSocietyName()) != null)
1462: return;
1463: // removeSocietyFromList(society);
1464: int answer = JOptionPane.showConfirmDialog(this ,
1465: "Delete society " + society.getSocietyName()
1466: + " from database?", "Delete Society",
1467: JOptionPane.YES_NO_OPTION);
1468: if (answer == JOptionPane.YES_OPTION) {
1469: try {
1470: // Will only actually do the delete
1471: // if no-one uses it
1472: PopulateDb.deleteSociety(society.getAssemblyId());
1473: } catch (Exception e) {
1474: if (log.isErrorEnabled()) {
1475: log.error("Exception deleting society "
1476: + society.getSocietyName() + " from db", e);
1477: }
1478: }
1479: }
1480: }
1481:
1482: protected void deleteRecipe() {
1483: deleteRecipeInNode(getSelectedNode());
1484: }
1485:
1486: /**
1487: * Ask user if recipe should be deleted everywhere;
1488: * if deleting everywhere, remove recipe from all experiments
1489: * in workspace and warn user to re-save them,
1490: * then ask user if recipe should be deleted from
1491: * the database.
1492: */
1493: private void deleteRecipeInNode(DefaultMutableTreeNode recipeNode) {
1494: RecipeComponent recipe = (RecipeComponent) recipeNode
1495: .getUserObject();
1496: model.removeNodeFromParent(recipeNode);
1497:
1498: // if deleted last reference to recipe in the workspace
1499: // ask if recipe should be deleted from database and delete it
1500: if (findNodeNamed(recipe.getRecipeName()) != null)
1501: return;
1502: try {
1503: PDbBase pdb = new PDbBase();
1504: try {
1505: int status = pdb.recipeExists(recipe);
1506: if (status == PDbBase.RECIPE_STATUS_EXISTS
1507: || status == PDbBase.RECIPE_STATUS_DIFFERS) {
1508: int answer = JOptionPane.showConfirmDialog(this ,
1509: "Delete recipe " + recipe.getRecipeName()
1510: + " from database?",
1511: "Delete Recipe", JOptionPane.YES_NO_OPTION);
1512: if (answer == JOptionPane.YES_OPTION) {
1513: // Even if the user wanted it removed, only remove
1514: // it if it is not in use, including any experiments
1515: // currently in the workspace
1516: // Bug 2032 -- not completely deleting complex recipes
1517: if (!pdb.isRecipeUsed(recipe))
1518: helper.deleteRecipe(recipe.getRecipeName());
1519: // pdb.removeLibRecipe(recipe);
1520: }
1521: }
1522: } finally {
1523: if (pdb != null)
1524: pdb.close();
1525: }
1526: } catch (Exception e) {
1527: JOptionPane
1528: .showMessageDialog(
1529: this ,
1530: "An exception occurred deleting the recipe from the database",
1531: "Error Writing Database",
1532: JOptionPane.ERROR_MESSAGE);
1533: if (log.isErrorEnabled()) {
1534: log.error("Error removing recipe from DB", e);
1535: }
1536: }
1537: recipeNames.remove(recipe.getRecipeName());
1538: }
1539:
1540: /**
1541: * Delete folder and recursively delete all its contents.
1542: */
1543:
1544: protected void deleteFolder() {
1545: DefaultMutableTreeNode node = getSelectedNode();
1546: if (node == root)
1547: return;
1548: if (node == null)
1549: return;
1550: int reply = JOptionPane
1551: .showConfirmDialog(this ,
1552: "Delete folder and all its contents?",
1553: "Delete Folder", JOptionPane.OK_CANCEL_OPTION,
1554: JOptionPane.PLAIN_MESSAGE);
1555: if (reply != JOptionPane.OK_OPTION)
1556: return;
1557:
1558: // if this folder is empty, just delete it
1559: if (node.isLeaf()) {
1560: folderNames.remove(node.getUserObject());
1561: model.removeNodeFromParent(node);
1562: return;
1563: }
1564:
1565: // make a list of nodes to delete
1566: // because we can't delete nodes while traversing the tree
1567: ArrayList nodesToDelete = new ArrayList();
1568: Enumeration nodes = node.breadthFirstEnumeration();
1569: nodes.nextElement(); // skip the first node which is this node
1570: while (nodes.hasMoreElements()) {
1571: DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) nodes
1572: .nextElement();
1573: nodesToDelete.add(childNode);
1574: }
1575: // delete the leaf nodes and user objects
1576: for (int i = 0; i < nodesToDelete.size(); i++) {
1577: DefaultMutableTreeNode nodeToDelete = (DefaultMutableTreeNode) nodesToDelete
1578: .get(i);
1579: Object o = nodeToDelete.getUserObject();
1580: if (o instanceof Experiment)
1581: deleteExperimentInNode(nodeToDelete);
1582: else if (o instanceof SocietyComponent)
1583: deleteSocietyInNode(nodeToDelete);
1584: else if (o instanceof RecipeComponent)
1585: deleteRecipeInNode(nodeToDelete);
1586: else if (o instanceof String) // an empty folder
1587: model.removeNodeFromParent(nodeToDelete); // just delete the tree node
1588: }
1589: // now we're left with the components the user decided not to delete
1590: // and the folder(s) containing them, but we need to
1591: // recursively delete the folder(s) that are empty or contain only
1592: // empty folders
1593: if (node.isLeaf()) {
1594: folderNames.remove(node.getUserObject());
1595: model.removeNodeFromParent(node);
1596: } else
1597: deleteEmptyFolders(node);
1598: }
1599:
1600: /**
1601: * Recursively delete empty folders or folders containing only
1602: * empty folders, starting with the given node.
1603: */
1604:
1605: private void deleteEmptyFolders(DefaultMutableTreeNode node) {
1606: ArrayList nodesToDelete = new ArrayList();
1607: Enumeration nodes = node.depthFirstEnumeration();
1608: nodes.nextElement(); // skip the first node which is the start node
1609: while (nodes.hasMoreElements()) {
1610: DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) nodes
1611: .nextElement();
1612: // if a node is a folder and a leaf, add it to the list to delete
1613: Object o = childNode.getUserObject();
1614: if (o instanceof String && childNode.isLeaf())
1615: nodesToDelete.add(childNode);
1616: }
1617: for (int i = 0; i < nodesToDelete.size(); i++)
1618: model
1619: .removeNodeFromParent((DefaultMutableTreeNode) nodesToDelete
1620: .get(i));
1621: // if original folder is now empty, just delete it
1622: if (node.isLeaf()) {
1623: folderNames.remove(node.getUserObject());
1624: model.removeNodeFromParent(node);
1625: return;
1626: }
1627: // if we deleted any nodes, then scan the tree again
1628: if (nodesToDelete.size() != 0)
1629: deleteEmptyFolders(node);
1630: }
1631:
1632: /**
1633: * Delete experiment from database and from workspace if it's there.
1634: * Don't allow deleting an experiment that's in the console,
1635: * because that would make it unrunnable.
1636: */
1637: protected void deleteExperimentFromDatabase() {
1638: Map experimentNamesMap = ExperimentDB.getExperimentNames();
1639: Set keys = experimentNamesMap.keySet();
1640: JComboBox cb = new JComboBox(keys.toArray(new String[keys
1641: .size()]));
1642: cb.setEditable(false);
1643: JPanel panel = new JPanel();
1644: panel.add(new JLabel("Delete Experiment:"));
1645: panel.add(cb);
1646: int result = JOptionPane.showConfirmDialog(null, panel,
1647: "Delete Experiment", JOptionPane.OK_CANCEL_OPTION,
1648: JOptionPane.PLAIN_MESSAGE);
1649: if (result != JOptionPane.OK_OPTION)
1650: return;
1651: String experimentName = (String) cb.getSelectedItem();
1652: if (findNodeNamed(experimentName) != null) {
1653: JOptionPane
1654: .showMessageDialog(
1655: this ,
1656: "You cannot delete an experiment that is in the workspace; delete it from the workspace first.",
1657: "Cannot Delete Experiment",
1658: JOptionPane.ERROR_MESSAGE);
1659: return;
1660: }
1661: String experimentId = (String) experimentNamesMap
1662: .get(experimentName);
1663: ExperimentDB.deleteExperiment(experimentId, experimentName);
1664: }
1665:
1666: /**
1667: * Delete recipe from database and from workspace if it's there.
1668: */
1669: protected void deleteRecipeFromDatabase() {
1670: Map recipeNamesHT = helper.getRecipeNamesFromDatabase();
1671: Set dbRecipeNames = recipeNamesHT.keySet();
1672: if (dbRecipeNames.isEmpty()) {
1673: JOptionPane.showMessageDialog(this ,
1674: "There are no recipes in the database",
1675: "No Database Recipes",
1676: JOptionPane.INFORMATION_MESSAGE);
1677: return;
1678: }
1679: JComboBox cb = new JComboBox(dbRecipeNames.toArray());
1680: cb.setEditable(false);
1681: JPanel panel = new JPanel();
1682: panel.add(new JLabel("Delete Recipe:"));
1683: panel.add(cb);
1684: int result = JOptionPane.showConfirmDialog(null, panel,
1685: "Delete Recipe", JOptionPane.OK_CANCEL_OPTION,
1686: JOptionPane.PLAIN_MESSAGE);
1687: if (result != JOptionPane.OK_OPTION)
1688: return;
1689: String recipeName = (String) cb.getSelectedItem();
1690: if (findNodeNamed(recipeName) != null) {
1691: JOptionPane
1692: .showMessageDialog(
1693: this ,
1694: "You cannot delete a recipe that is in the workspace; delete it from the workspace first.",
1695: "Cannot Delete Recipe",
1696: JOptionPane.ERROR_MESSAGE);
1697: return;
1698: }
1699: try {
1700: helper.deleteRecipe(recipeName);
1701: } catch (Exception e) {
1702: JOptionPane
1703: .showMessageDialog(
1704: this ,
1705: "An exception occurred deleting the recipe from the database",
1706: "Error Writing Database",
1707: JOptionPane.ERROR_MESSAGE);
1708: }
1709: }
1710:
1711: ////////////////////////////////
1712: // Save Experiment or Society in Database
1713: ///////////////////////////////
1714:
1715: private void saveInDatabase() {
1716: DefaultMutableTreeNode node = getSelectedNode();
1717: if (node == null)
1718: return;
1719: Object o = node.getUserObject();
1720: if (o == null)
1721: return;
1722: if (o instanceof Experiment)
1723: saveExperiment((DBExperiment) o);
1724: else if (o instanceof SocietyComponent)
1725: saveSociety((SocietyComponent) o);
1726: else if (o instanceof RecipeComponent)
1727: saveRecipe((RecipeComponent) o);
1728: }
1729:
1730: private void saveExperiment(DBExperiment experiment) {
1731: final DBExperiment exp = experiment;
1732: GUIUtils.timeConsumingTaskStart(organizer);
1733: try {
1734: new Thread("Save") {
1735: public void run() {
1736: exp.save(saveToDbConflictHandler);
1737: GUIUtils.timeConsumingTaskEnd(organizer);
1738: }
1739: }.start();
1740: } catch (RuntimeException re) {
1741: if (log.isErrorEnabled()) {
1742: log.error("Error saving experiment: ", re);
1743: }
1744: GUIUtils.timeConsumingTaskEnd(organizer);
1745: }
1746: }
1747:
1748: private void saveSociety(SocietyComponent society) {
1749: final SocietyComponent sc = society;
1750: GUIUtils.timeConsumingTaskStart(organizer);
1751: try {
1752: new Thread("SaveSociety") {
1753: public void run() {
1754: boolean success = sc.saveToDatabase();
1755: GUIUtils.timeConsumingTaskEnd(organizer);
1756: if (!success && organizer.log.isWarnEnabled()) {
1757: organizer.log.warn("Failed to save society "
1758: + sc.getSocietyName());
1759: } else if (organizer.log.isDebugEnabled()) {
1760: organizer.log.debug("Saved society "
1761: + sc.getSocietyName());
1762: }
1763: }
1764: }.start();
1765: } catch (RuntimeException re) {
1766: if (log.isErrorEnabled()) {
1767: log.error("Runtime exception saving society", re);
1768: }
1769: GUIUtils.timeConsumingTaskEnd(organizer);
1770: }
1771: GUIUtils.timeConsumingTaskEnd(organizer);
1772: }
1773:
1774: private void saveRecipe(RecipeComponent recipe) {
1775: recipe.saveToDatabase();
1776: }
1777:
1778: ////////////////////////////////
1779: // Replace Component in Experiment
1780: ///////////////////////////////
1781:
1782: public void replaceComponent(Experiment experiment,
1783: ModifiableComponent component,
1784: ModifiableComponent newComponent) {
1785: // find experiment node
1786: DefaultMutableTreeNode expNode = findNode(experiment);
1787: if (expNode == null) {
1788: if (log.isErrorEnabled()) {
1789: log.error("Experiment is not in tree: "
1790: + experiment.getExperimentName());
1791: }
1792: return;
1793: }
1794: // find component node in experiment
1795: DefaultMutableTreeNode componentNode = null;
1796: Enumeration nodes = expNode.depthFirstEnumeration();
1797: while (nodes.hasMoreElements()) {
1798: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
1799: .nextElement();
1800: if (node.getUserObject().equals(component)) {
1801: componentNode = node;
1802: break;
1803: }
1804: }
1805: if (componentNode == null) {
1806: if (log.isErrorEnabled()) {
1807: log.error("Component is not in experiment: "
1808: + component.getShortName());
1809: }
1810: return;
1811: }
1812: model.removeNodeFromParent(componentNode);
1813: if (component instanceof SocietyComponent) {
1814: societyNames.remove(component.getShortName());
1815: addSocietyToWorkspace((SocietyComponent) newComponent,
1816: expNode);
1817: } else if (component instanceof RecipeComponent) {
1818: recipeNames.remove(component.getShortName());
1819: addRecipeToWorkspace((RecipeComponent) newComponent,
1820: expNode);
1821: }
1822: }
1823:
1824: ////////////////////////////////
1825: // Add and remove nodes for children of an experiment
1826: ////////////////////////////////
1827:
1828: /**
1829: * Add tree nodes for the children (societies and recipes)
1830: * of an experiment. First removes any current children nodes.
1831: * @param experiment the experiment for which to add children
1832: */
1833: public void addChildren(Experiment experiment) {
1834: DefaultMutableTreeNode expNode = findNode(experiment);
1835: // first remove old nodes
1836: int n = expNode.getChildCount();
1837: for (int i = 0; i < n; i++) {
1838: DefaultMutableTreeNode node = (DefaultMutableTreeNode) expNode
1839: .getChildAt(0);
1840: model.removeNodeFromParent(node);
1841: }
1842: SocietyComponent society = experiment.getSocietyComponent();
1843: if (society != null)
1844: addSocietyToWorkspace(society, expNode);
1845: RecipeComponent[] recipes = experiment.getRecipeComponents();
1846: for (int i = 0; i < recipes.length; i++)
1847: addRecipeToWorkspace(recipes[i], expNode);
1848: workspace.setSelection(expNode);
1849: }
1850:
1851: /**
1852: * Display tree nodes for the children (societies and recipes)
1853: * of an experiment in gray and don't let them be edited.
1854: * Used when editing the experiment.
1855: * @param experiment the experiment
1856: */
1857: protected void removeChildren(Experiment experiment) {
1858: DefaultMutableTreeNode expNode = findNode(experiment);
1859: int n = expNode.getChildCount();
1860: // display these nodes in gray and don't let them be edited
1861: for (int i = 0; i < n; i++) {
1862: DefaultMutableTreeNode node = (DefaultMutableTreeNode) expNode
1863: .getChildAt(i);
1864: model.nodeChanged(node);
1865: }
1866: }
1867:
1868: ////////////////////////////////
1869: // Display names of experiments containing societies or recipes
1870: ///////////////////////////////
1871:
1872: /**
1873: * Display names of experiments in workspace or database
1874: * that contain the indicated society.
1875: */
1876: public void displayExperiments(SocietyComponent society) {
1877: Set experimentNames = DBUtils
1878: .dbGetExperimentsWithSociety(society.getSocietyName());
1879: experimentNames.addAll(getExperimentNamesInWorkspace(society));
1880: if (experimentNames.size() == 0)
1881: return; // no experiments were affected
1882: displayExperimentList(experimentNames);
1883: }
1884:
1885: private void displayExperimentsInWorkspace(SocietyComponent society) {
1886: ArrayList experimentNames = getExperimentNamesInWorkspace(society);
1887: if (experimentNames.size() == 0)
1888: return; // no experiments were affected
1889: displayExperimentList(experimentNames);
1890: }
1891:
1892: /**
1893: * Display names of experiments in workspace or database
1894: * that contain the indicated recipe.
1895: * @param recipe the recipe component
1896: */
1897: public void displayExperiments(RecipeComponent recipe) {
1898: Set experimentNames = DBUtils.dbGetExperimentsWithRecipe(recipe
1899: .getRecipeName());
1900: experimentNames.addAll(getExperimentNamesInWorkspace(recipe));
1901: if (experimentNames.size() == 0)
1902: return; // no experiments were affected
1903: displayExperimentList(experimentNames);
1904: }
1905:
1906: private void displayExperimentsInWorkspace(RecipeComponent recipe) {
1907: ArrayList experimentNames = getExperimentNamesInWorkspace(recipe);
1908: if (experimentNames.size() == 0)
1909: return; // no experiments were affected
1910: displayExperimentList(experimentNames);
1911: }
1912:
1913: /**
1914: * Get experiments from workspace.
1915: */
1916: private ArrayList getExperiments() {
1917: ArrayList experiments = new ArrayList();
1918: Enumeration nodes = root.depthFirstEnumeration();
1919: while (nodes.hasMoreElements()) {
1920: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
1921: .nextElement();
1922: Object o = node.getUserObject();
1923: if (o instanceof Experiment)
1924: experiments.add(o);
1925: }
1926: return experiments;
1927: }
1928:
1929: /**
1930: * Get names of experiments in workspace that contain the society.
1931: * Mark the experiments as modified.
1932: */
1933: private ArrayList getExperimentNamesInWorkspace(
1934: SocietyComponent society) {
1935: ArrayList results = new ArrayList();
1936: ArrayList experiments = getExperiments();
1937: for (int i = 0; i < experiments.size(); i++) {
1938: Experiment experiment = (Experiment) experiments.get(i);
1939: SocietyComponent sc = experiment.getSocietyComponent();
1940: if (sc != null && sc.equals(society))
1941: results.add(experiment.getExperimentName());
1942: }
1943: return results;
1944: }
1945:
1946: /**
1947: * Get names of experiments in workspace that contain the recipe.
1948: * Mark the experiments as modified.
1949: */
1950: private ArrayList getExperimentNamesInWorkspace(
1951: RecipeComponent recipe) {
1952: ArrayList results = new ArrayList();
1953: ArrayList experiments = getExperiments();
1954: for (int i = 0; i < experiments.size(); i++) {
1955: Experiment experiment = (Experiment) experiments.get(i);
1956: RecipeComponent[] recipes = experiment
1957: .getRecipeComponents();
1958: for (int j = 0; j < recipes.length; j++) {
1959: if (recipes[j].equals(recipe)) {
1960: results.add(experiment.getExperimentName());
1961: break;
1962: }
1963: }
1964: }
1965: return results;
1966: }
1967:
1968: /**
1969: * Display the names of experiments affected by a change
1970: * to their society or recipes.
1971: */
1972: private void displayExperimentList(Collection names) {
1973: Frame frame = null;
1974: try {
1975: frame = JOptionPane.getFrameForComponent(this );
1976: } catch (Exception e) {
1977: if (log.isErrorEnabled()) {
1978: log.error("Could not get frame for Organizer: " + e);
1979: }
1980: }
1981: final JDialog dialog = new JDialog(frame,
1982: "Experiments That Must Be Saved", true);
1983: dialog.getContentPane().setLayout(new BorderLayout());
1984: JPanel panel = new JPanel();
1985: panel.setLayout(new GridBagLayout());
1986: int x = 0;
1987: int y = 0;
1988: JTextArea msg = new JTextArea(
1989: "The following experiments must be saved to the database in order to be updated:",
1990: 3, 100);
1991: msg.setLineWrap(true);
1992: msg.setWrapStyleWord(true);
1993: msg.setBackground(panel.getBackground());
1994: panel.add(msg, new GridBagConstraints(x, y++, 1, 1, 0.0, 0.0,
1995: GridBagConstraints.CENTER, GridBagConstraints.NONE,
1996: new Insets(10, 5, 5, 5), 0, 0));
1997: Vector sortedNames = new Vector(names);
1998: Collections.sort(sortedNames);
1999: JList namesList = new JList(sortedNames);
2000: namesList.setBackground(panel.getBackground());
2001: JScrollPane jsp = new JScrollPane(namesList);
2002: jsp.setMinimumSize(new Dimension(100, 50));
2003: panel.add(jsp, new GridBagConstraints(x, y++, 1, 1, 1.0, 1.0,
2004: GridBagConstraints.CENTER, GridBagConstraints.BOTH,
2005: new Insets(0, 5, 0, 5), 0, 0));
2006: JButton okButton = new JButton("OK");
2007: okButton.addActionListener(new ActionListener() {
2008: public void actionPerformed(ActionEvent e) {
2009: dialog.dispose();
2010: }
2011: });
2012: JPanel buttonPanel = new JPanel();
2013: buttonPanel.add(okButton);
2014: dialog.getContentPane().add(panel, BorderLayout.CENTER);
2015: dialog.getContentPane().add(buttonPanel, BorderLayout.SOUTH);
2016: dialog.setSize(400, 300);
2017: dialog.setVisible(true);
2018: }
2019:
2020: ////////////////////////////////
2021: // Utilities
2022: ///////////////////////////////
2023:
2024: private void installListeners(ModifiableComponent component) {
2025: component.addModificationListener(myModificationListener);
2026: }
2027:
2028: /**
2029: * The following methods:
2030: * create a new tree node for the component,
2031: * add the new node to the workspace as the last child of the specified node,
2032: * add the component name to the appropriate list of unique names,
2033: * add the listeners for the component, and
2034: * set the workspace selection to be the new node.
2035: */
2036: private DefaultMutableTreeNode addExperimentToWorkspace(
2037: DBExperiment experiment, DefaultMutableTreeNode node) {
2038: DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
2039: experiment, true);
2040: addNode(node, newNode);
2041: experimentNames.add(experiment.getExperimentName());
2042: workspace.setSelection(newNode);
2043: installListeners(experiment);
2044: return newNode;
2045: }
2046:
2047: private DefaultMutableTreeNode addSocietyToWorkspace(
2048: SocietyComponent sc, DefaultMutableTreeNode node) {
2049: DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(sc,
2050: false);
2051: addNode(node, newNode);
2052: societyNames.add(sc.getSocietyName());
2053: workspace.setSelection(newNode);
2054: installListeners(sc);
2055: return newNode;
2056: }
2057:
2058: protected DefaultMutableTreeNode addRecipeToWorkspace(
2059: RecipeComponent recipe, DefaultMutableTreeNode node) {
2060: if (recipe == null)
2061: return null;
2062: DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
2063: recipe, false);
2064: addNode(node, newNode);
2065: recipeNames.add(recipe.getRecipeName());
2066: workspace.setSelection(newNode);
2067: installListeners(recipe);
2068: return newNode;
2069: }
2070:
2071: protected DefaultMutableTreeNode addFolderToWorkspace(String name,
2072: DefaultMutableTreeNode node) {
2073: DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
2074: name, true);
2075: addNode(node, newNode);
2076: folderNames.add(name);
2077: workspace.setSelection(newNode);
2078: return newNode;
2079: }
2080:
2081: /**
2082: * Add newNode as last child of node.
2083: */
2084: private void addNode(DefaultMutableTreeNode node,
2085: DefaultMutableTreeNode newNode) {
2086: if (node == null)
2087: node = root;
2088: model.insertNodeInto(newNode, node, node.getChildCount());
2089: workspace.scrollPathToVisible(new TreePath(newNode.getPath()));
2090: }
2091:
2092: /**
2093: * Find a node containing the user object.
2094: * WARNING: societies and recipes may be in more than one node,
2095: * this just returns the first node found
2096: */
2097: private DefaultMutableTreeNode findNode(Object userObject) {
2098: Enumeration nodes = root.depthFirstEnumeration();
2099: while (nodes.hasMoreElements()) {
2100: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
2101: .nextElement();
2102: if (node.getUserObject().equals(userObject))
2103: return node;
2104: }
2105: return null;
2106: }
2107:
2108: private DefaultMutableTreeNode findNodeNamed(String s) {
2109: Enumeration nodes = root.depthFirstEnumeration();
2110: while (nodes.hasMoreElements()) {
2111: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
2112: .nextElement();
2113: if (node.toString().equals(s))
2114: return node;
2115: }
2116: return null;
2117: }
2118:
2119: /**
2120: * If the selected node is in an experiment, return true.
2121: */
2122: private boolean isSelectedNodeInExperiment() {
2123: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) getSelectedNode()
2124: .getParent();
2125: if (parentNode.getUserObject() != null
2126: && parentNode.getUserObject() instanceof Experiment)
2127: return true;
2128: return false;
2129: }
2130:
2131: /**
2132: * Fully expand the tree; called in initialization
2133: * so that the initial view of the tree is fully expanded.
2134: */
2135: private void expandTree() {
2136: Enumeration nodes = root.depthFirstEnumeration();
2137: while (nodes.hasMoreElements()) {
2138: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
2139: .nextElement();
2140: workspace.expandPath(new TreePath(node.getPath()));
2141: }
2142: }
2143:
2144: /**
2145: * Returns true if the object is
2146: * a component (a society or recipe) in an experiment
2147: * that is being built or run.
2148: */
2149: protected boolean isComponentInUse(ModifiableComponent mc) {
2150: Enumeration nodes = root.depthFirstEnumeration();
2151: while (nodes.hasMoreElements()) {
2152: DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodes
2153: .nextElement();
2154: Object o = node.getUserObject();
2155: if (o instanceof Experiment) {
2156: Experiment exp = (Experiment) o;
2157: if (CSMART.isExperimentInConsole(exp)
2158: || CSMART.isExperimentInEditor(exp))
2159: if (exp.getComponents().contains(mc))
2160: return true;
2161: }
2162: }
2163: return false;
2164: }
2165:
2166: /**
2167: * Returns true if the node is in an experiment node
2168: * and the experiment is being built or run.
2169: */
2170: protected boolean isNodeInUse(DefaultMutableTreeNode node) {
2171: DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode) node
2172: .getParent();
2173: if (parentNode == null)
2174: return false;
2175: Object parentObject = parentNode.getUserObject();
2176: if (parentObject instanceof Experiment) {
2177: Experiment exp = (Experiment) parentObject;
2178: if (CSMART.isExperimentInConsole(exp)
2179: || CSMART.isExperimentInEditor(exp))
2180: return true;
2181: }
2182: return false;
2183: }
2184:
2185: ////////////////////////////////
2186: // Title utilities
2187: ///////////////////////////////
2188:
2189: private void setFrameTitle() {
2190: setFrameTitle(workspace.getSelectionPath());
2191: }
2192:
2193: private void setFrameTitle(TreePath newSelection) {
2194: String doc;
2195: if (newSelection != null) {
2196: doc = newSelection.toString();
2197: } else {
2198: doc = null;
2199: }
2200: setFrameTitleDocument(doc);
2201: }
2202:
2203: private void setFrameTitleDocument(String doc) {
2204: JFrame frame = (JFrame) SwingUtilities.getAncestorOfClass(
2205: JFrame.class, workspace);
2206: if (frame == null)
2207: return;
2208: String oldTitle = frame.getTitle();
2209: if (oldTitle == null) {
2210: oldTitle = FRAME_TITLE;
2211: } else {
2212: int colon = oldTitle.indexOf(':');
2213: if (colon >= 0)
2214: oldTitle = oldTitle.substring(0, colon).trim();
2215: }
2216: if (doc == null)
2217: frame.setTitle(oldTitle);
2218: else
2219: frame.setTitle(oldTitle + ":" + doc);
2220: }
2221:
2222: ///////////////////////////////////////////
2223: // workspace listeners
2224: ///////////////////////////////////////////
2225:
2226: private TreeModelListener myModelListener = new TreeModelListener() {
2227: public void treeNodesChanged(TreeModelEvent e) {
2228: if (doWorkspace)
2229: update();
2230: }
2231:
2232: public void treeNodesInserted(TreeModelEvent e) {
2233: if (doWorkspace)
2234: update();
2235: }
2236:
2237: public void treeNodesRemoved(TreeModelEvent e) {
2238: if (doWorkspace)
2239: update();
2240: }
2241:
2242: public void treeStructureChanged(TreeModelEvent e) {
2243: if (doWorkspace)
2244: update();
2245: }
2246: };
2247:
2248: /**
2249: * Called when selection in organizer tree is changed.
2250: */
2251: private TreeSelectionListener mySelectionListener = new TreeSelectionListener() {
2252: public void valueChanged(TreeSelectionEvent e) {
2253: setFrameTitle(e.getNewLeadSelectionPath());
2254: }
2255: };
2256:
2257: private AncestorListener myAncestorListener = new AncestorListener() {
2258: public void ancestorAdded(AncestorEvent e) {
2259: setFrameTitle();
2260: }
2261:
2262: public void ancestorRemoved(AncestorEvent e) {
2263: }
2264:
2265: public void ancestorMoved(AncestorEvent e) {
2266: }
2267: };
2268:
2269: protected void addTreeSelectionListener(
2270: TreeSelectionListener listener) {
2271: workspace.addTreeSelectionListener(listener);
2272: }
2273:
2274: protected void addTreeModelListener(TreeModelListener listener) {
2275: model.addTreeModelListener(listener);
2276: }
2277:
2278: ///////////////////////////////////////
2279: // Restore (read from file) and save workspace.
2280: ///////////////////////////////////////
2281:
2282: private void restore(String fileName) {
2283: try {
2284: if (!fileName.endsWith(".xml"))
2285: fileName = fileName + ".xml";
2286: this .workspaceFileName = fileName;
2287:
2288: root = new DefaultMutableTreeNode();
2289: String label = workspaceFileName;
2290: label = label.substring(0, label.lastIndexOf('.'));
2291: root.setUserObject(label);
2292:
2293: // Must set up the model, workspace before actually doing the
2294: // load since the helper methods need this infrastructure
2295: model = new DefaultTreeModel(root);
2296:
2297: if (doWorkspace) {
2298: // set the selected node to be the root node
2299: workspace = new OrganizerTree(model);
2300: workspace.setSelection(root);
2301: }
2302:
2303: try {
2304: experimentNames.init(root, DBExperiment.class,
2305: "getExperimentName");
2306: societyNames.init(root, SocietyComponent.class,
2307: "getSocietyName");
2308: recipeNames.init(root, RecipeComponent.class,
2309: "getRecipeName");
2310: folderNames.init(root, String.class, "toString");
2311: } catch (SecurityException se) {
2312: if (log.isErrorEnabled()) {
2313: log
2314: .error(
2315: "Organizer error restoring workspace",
2316: se);
2317: }
2318: }
2319:
2320: if (doWorkspace) {
2321: OrganizerXML oxml = new OrganizerXML();
2322: oxml.populateWorkspace(fileName, this );
2323: } else {
2324: if (log.isInfoEnabled()) {
2325: log.info("Not reading workspace file " + fileName);
2326: }
2327: }
2328:
2329: } catch (Exception ioe) {
2330: if (log.isErrorEnabled()) {
2331: log.error("Organizer error restoring workspace", ioe);
2332: }
2333: return;
2334: }
2335: } // end of restore
2336:
2337: /**
2338: * Call update if anything changed.
2339: */
2340: private transient ModificationListener myModificationListener = new ModificationListener() {
2341: public void modified(ModificationEvent event) {
2342: DefaultMutableTreeNode changedNode = findNode(event
2343: .getSource());
2344: if (changedNode != null) {
2345: model.nodeChanged(changedNode);
2346: // this could be an experiment saved, which would make it runnable
2347: // which should change which tools are enabled
2348: csmart.enableCSMARTTools();
2349: }
2350: if (doWorkspace)
2351: update();
2352: }
2353: };
2354:
2355: protected boolean exitAllowed() {
2356: if (doWorkspace) {
2357: synchronized (lockObject) {
2358: if (updateNeeded) {
2359: nextUpdate = System.currentTimeMillis();
2360: lockObject.notify();
2361: while (updateNeeded) {
2362: try {
2363: lockObject.wait();
2364: } catch (InterruptedException ie) {
2365: }
2366: }
2367: }
2368: }
2369: }
2370: return true;
2371: }
2372:
2373: /**
2374: * Force an update, which saves the current workspace.
2375: */
2376: protected void save() {
2377: if (doWorkspace)
2378: update();
2379: }
2380:
2381: private void save(String fileName) {
2382: if (!doWorkspace)
2383: return;
2384:
2385: if (log.isDebugEnabled()) {
2386: log.debug("Saving to: " + fileName);
2387: }
2388:
2389: if (!fileName.endsWith(".xml"))
2390: fileName = fileName + ".xml";
2391:
2392: try {
2393: // Write out the workspace as an XML file
2394: OrganizerXML oxml = new OrganizerXML();
2395: try {
2396: oxml.writeXMLFile(new File("."), oxml
2397: .createXMLDocument(root, csmart.getResultDir()
2398: .getPath()), fileName);
2399: } catch (IOException ioe) {
2400: if (log.isErrorEnabled()) {
2401: log
2402: .error(
2403: "Caught an Exception trying to write Organizer XML File",
2404: ioe);
2405: }
2406: }
2407:
2408: } catch (Exception ioe) {
2409: if (log.isErrorEnabled()) {
2410: log.error("Organizer error saving workspace", ioe);
2411: }
2412: }
2413: }
2414:
2415: private Thread updater = new Thread() {
2416: public void start() {
2417: updateNeeded = false;
2418: if (doWorkspace)
2419: super .start();
2420: }
2421:
2422: public void run() {
2423: if (!doWorkspace)
2424: return;
2425: synchronized (lockObject) {
2426: while (true) {
2427: try {
2428: long now = System.currentTimeMillis();
2429: if (updateNeeded && now > nextUpdate) {
2430: save(workspaceFileName);
2431: updateNeeded = false;
2432: lockObject.notifyAll(); // In case anyone's waiting for the update to finish
2433: } else if (updateNeeded) {
2434: long delay = nextUpdate - now;
2435: if (delay > 0) {
2436: lockObject.wait(delay);
2437: }
2438: } else {
2439: lockObject.wait();
2440: }
2441: } catch (InterruptedException ie) {
2442: }
2443: }
2444: }
2445: }
2446: };
2447:
2448: private void update() {
2449: if (!doWorkspace)
2450: return;
2451: synchronized (lockObject) {
2452: nextUpdate = System.currentTimeMillis() + UPDATE_DELAY;
2453: updateNeeded = true;
2454: lockObject.notify();
2455: }
2456: }
2457:
2458: ////////////////////////////////////////////
2459:
2460: // Class for holding name/Class pairs in UIs
2461: private static class NameClassItem {
2462: public String name;
2463: public Class cls;
2464:
2465: public NameClassItem(String name, Class cls) {
2466: this .cls = cls;
2467: this .name = name;
2468: }
2469:
2470: public String toString() {
2471: return name;
2472: }
2473: }
2474:
2475: private void readObject(ObjectInputStream ois) throws IOException,
2476: ClassNotFoundException {
2477: ois.defaultReadObject();
2478: createLogger();
2479: }
2480:
2481: } // end of Organizer class
|