001 /* 002 * CRIMSON 003 * Copyright (c) 2006, Stephen Fisher, Susan Davidson, and Junhyong Kim, 004 * University of Pennsylvania. 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License as 008 * published by the Free Software Foundation; either version 2 of the 009 * License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, but 012 * WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 * 02110-1301 USA. 020 * 021 * @(#)TreeUtils.java 022 */ 023 024 package edu.upenn.crimson.gui; 025 026 import edu.upenn.crimson.*; 027 import edu.upenn.crimson.io.*; 028 import java.io.File; 029 import java.util.ArrayList; 030 import java.util.Iterator; 031 import java.awt.*; 032 import java.awt.event.*; 033 import javax.swing.*; 034 035 /** 036 * Static methods used throughout the GUI for Tree objects. 037 * 038 * @author Stephen Fisher 039 * @version $Id: TreeUtils.java,v 1.16 2007/10/09 20:14:16 fisher Exp $ 040 */ 041 042 public class TreeUtils { 043 044 //-------------------------------------------------------------------------- 045 // Miscellaneous Methods 046 047 /** Present a dialog box for selecting a particular Tree. */ 048 public static String treeSelector() { 049 // use this to get the return value from ObjectSelectorDialog 050 ArrayList out = new ArrayList(); 051 Object[] objects = ObjectHandles.getTrees().toArray(); 052 new ObjectSelectorDialog("Tree Selector", objects, out); 053 054 if (out.size() > 0) { 055 return (String) out.get(0); 056 } else { 057 return ""; 058 } 059 } 060 061 /** 062 * This will process the NEXUS file selected and then load the 063 * file into the specified database table. 064 */ 065 public static void loadTree() { 066 // if no database connection then error 067 if (! Database.isOpen()) { 068 CrimsonUtils.printError("Must open a database before loading a tree."); 069 return; 070 } 071 072 // get file containing tree 073 File file = GUIUtils.openFileChooser("Select NEXUS File", null); 074 075 // no valid file selected 076 if (file == null) return; 077 078 // use filename as tree/partition IDs - without the .nex 079 // extension, if it exists. 080 String name = file.getName(); 081 if (name.endsWith(".nex")) name = name.substring(0, name.length()-4); 082 loadTree(file, name, name); 083 } 084 085 public static void loadTree(File file, String treeID, String partitionID) { 086 // if invalid file then try again 087 if (file == null) loadTree(); 088 089 // get tree/partition names 090 String[] labels = {" Tree name:", " Partition name:"}; 091 092 // use this to get the return value from FieldEditDialog 093 ArrayList textFields = new ArrayList(); 094 095 // get the value the user entered 096 JTextField treeTF = new JTextField(20); 097 treeTF.setText(treeID); 098 textFields.add(treeTF); 099 JTextField partitionTF = new JTextField(20); 100 partitionTF.setText(partitionID); 101 textFields.add(partitionTF); 102 103 new FieldEditDialog("Load Tree", labels, textFields); 104 105 // check "exit code" for FieldEditDialog, if false then exit 106 Boolean exitCode = (Boolean) textFields.get(textFields.size()-1); 107 if (! exitCode.booleanValue()) return; 108 109 // get tree/partition names 110 treeID = treeTF.getText().toUpperCase(); 111 partitionID = partitionTF.getText().toUpperCase(); 112 113 // shouldn't need to test both database and treePool, but 114 // better to be careful 115 if (Trees.dbContains(treeID) || ObjectHandles.containsTree(treeID)) { 116 JOptionPane.showMessageDialog(null, 117 "Tree name already used, please select a different name", 118 "Name Error", JOptionPane.INFORMATION_MESSAGE); 119 120 // rerun the tree/partition ID dialog 121 loadTree(file, treeID, partitionID); 122 return; 123 } 124 if (Partitions.dbContains(partitionID) || ObjectHandles.containsPartition(partitionID)) { 125 JOptionPane.showMessageDialog(null, 126 "Partition name already used, please select a different name", 127 "Name Error", JOptionPane.INFORMATION_MESSAGE); 128 129 // rerun the tree/partition ID dialog 130 loadTree(file, treeID, partitionID); 131 return; 132 } 133 134 // convert any '.' to '_' in treeID since table names can not 135 // contain '.' 136 String origTreeID = treeID; 137 treeID = treeID.replace('.', '_'); 138 if (treeID.compareTo(origTreeID) != 0) { 139 CrimsonUtils.printWarning("Tree names can not contain '.' and were converted to '_'."); 140 } 141 142 String msg = "loadTree(\"" + GUIUtils.getFilename(file); 143 msg += "\", \"" + treeID + "\", \"" + partitionID + "\")"; 144 Root.runCommand(msg); 145 } 146 147 /** 148 * This will process the NEXUS file selected and then append the 149 * file into the specified database table. 150 */ 151 public static void appendTree(String treeID) { 152 // if no database connection then error 153 if (! Database.isOpen()) { 154 CrimsonUtils.printError("Must open a database before appending data to a tree."); 155 return; 156 } 157 158 if (CrimsonUtils.isEmpty(treeID)) { 159 treeID = treeSelector(); 160 161 // if the user didn't select a tree, then quit 162 if (CrimsonUtils.isEmpty(treeID)) return; 163 } 164 165 // get file containing tree 166 File file = GUIUtils.openFileChooser("Select NEXUS File", null); 167 168 // no valid file selected 169 if (file == null) return; 170 171 // get tree/partition names 172 String[] labels = {" Tree name:", " Partition name:"}; 173 174 // use this to get the return value from FieldEditDialog 175 ArrayList textFields = new ArrayList(); 176 177 // get the value the user entered 178 JTextField treeTF = new JTextField(20); 179 treeTF.setText(treeID); 180 treeTF.setEnabled(false); 181 textFields.add(treeTF); 182 JTextField partitionTF = new JTextField(20); 183 // use filename as partition ID, without the .nex extension, 184 // if it exists. 185 String name = file.getName(); 186 if (name.endsWith(".nex")) name = name.substring(0, name.length()-4); 187 partitionTF.setText(name); 188 textFields.add(partitionTF); 189 190 new FieldEditDialog("Append Tree", labels, textFields); 191 192 // check "exit code" for FieldEditDialog, if false then exit 193 Boolean exitCode = (Boolean) textFields.get(textFields.size()-1); 194 if (! exitCode.booleanValue()) return; 195 196 // get tree/partition names 197 String partitionID = partitionTF.getText().toUpperCase(); 198 199 String msg = "appendTree(\"" + GUIUtils.getFilename(file); 200 msg += "\", \"" + treeID + "\", \"" + partitionID + "\")"; 201 Root.runCommand(msg); 202 } 203 204 /** This will export the tree specified by TreeSelector(). */ 205 public static void exportTree(String id) { 206 // if no database connection then error 207 if (! Database.isOpen()) { 208 CrimsonUtils.printError("Must open a database before exporting a tree."); 209 return; 210 } 211 212 if (CrimsonUtils.isEmpty(id)) { 213 id = treeSelector(); 214 215 // if ID still empty then quit 216 if (CrimsonUtils.isEmpty(id)) return; 217 } 218 219 Tree tree = ObjectHandles.getTree(id); 220 if (tree == null) { 221 CrimsonUtils.printError("Invalid tree: " + id); 222 return; 223 } 224 225 // if no partitions then only export structure, else ask user 226 if (tree.getNumPartitions() == 0) { 227 Root.runCommand("exportStruct(\"" + id + "\")"); 228 } else { 229 String msg = "Do you want to include the partition data?\n"; 230 msg += "Tree: " + id; 231 Object[] options = {"Structure and Data", "Only Structure", "Cancel"}; 232 int flag = JOptionPane.showOptionDialog(null, msg, 233 "Export Tree", 234 JOptionPane.YES_NO_CANCEL_OPTION, 235 JOptionPane.QUESTION_MESSAGE, 236 null, 237 options, 238 options[1]); 239 if (flag == JOptionPane.YES_OPTION) { // include data 240 Root.runCommand("exportTree(\"" + id + "\")"); 241 } else if (flag == JOptionPane.NO_OPTION) { // no data 242 Root.runCommand("exportStruct(\"" + id + "\")"); 243 } 244 } 245 } 246 247 /** This will delete the tree specified by TreeSelector(). */ 248 public static void deleteTree(String id) { 249 if (CrimsonUtils.isEmpty(id)) { 250 id = treeSelector(); 251 252 // if ID still empty then quit 253 if (CrimsonUtils.isEmpty(id)) return; 254 } 255 256 id = id.toUpperCase(); 257 String queries = ""; 258 for (Iterator i = ObjectHandles.queryIterator(); i.hasNext();) { 259 Query query = (Query) i.next(); 260 if (id.equals(query.getTreeID())) queries += " Query: " + query.getID() + "\n"; 261 } 262 263 String msg = "Are you sure you want to remove this tree?\n"; 264 msg += " Tree: " + id; 265 266 if (! CrimsonUtils.isEmpty(queries)) { 267 msg = "Queries exist that reference this tree. These queries will be deleted.\n" 268 + queries + "\n" + msg; 269 } 270 271 Object[] options = {"Delete Tree", "Cancel"}; 272 int flag = JOptionPane.showOptionDialog(null, msg, 273 "Delete Confirmation", 274 JOptionPane.YES_NO_OPTION, 275 JOptionPane.QUESTION_MESSAGE, 276 null, 277 options, 278 options[1]); 279 if (flag == JOptionPane.YES_OPTION) { // "Delete" 280 // delete tree 281 Root.runCommand("deleteTree(\"" + id + "\", 1)"); 282 } 283 } 284 285 public static boolean isTreeBuilt(Tree tree) { 286 if (tree.isBuilt()) { 287 return true; 288 } else { 289 // if less than 1000 leaves, just build the tree 290 if (tree.getNumLeaves() < 1000) { 291 Root.runCommand("getTree(\"" + tree.getID() + "\").buildTree()"); 292 return true; 293 } 294 295 String msg = "The tree structure " + tree.getID() + " needs to be created.\n"; 296 msg += "This may take a few minutes for very large trees.\n"; 297 msg += "Do you want to continue?\n"; 298 Object[] options = {"Ok", "Cancel"}; 299 int flag = JOptionPane.showOptionDialog(null, msg, 300 "Build Tree", 301 JOptionPane.YES_NO_OPTION, 302 JOptionPane.QUESTION_MESSAGE, 303 null, 304 options, 305 options[0]); 306 if (flag == JOptionPane.YES_OPTION) { // build tree 307 Root.runCommand("getTree(\"" + tree.getID() + "\").buildTree()"); 308 return true; 309 } 310 } 311 312 // user pressed cancel button 313 return false; 314 } 315 316 /** This will view the tree specified by TreeSelector(). */ 317 public static void view3DTree(String id) { 318 // don't do anything if WALRUS not loaded 319 if (! CrimsonMain.WALRUS) return; 320 321 // if no database connection then error 322 if (! Database.isOpen()) { 323 CrimsonUtils.printError("Must open a database before viewing a tree."); 324 return; 325 } 326 327 // if no ID, then use treeSelector() to get tree 328 if (CrimsonUtils.isEmpty(id)) { 329 id = treeSelector(); 330 331 // if ID still empty then quit 332 if (CrimsonUtils.isEmpty(id)) return; 333 } 334 335 Tree tree = ObjectHandles.getTree(id); 336 // make sure tree exists 337 if (tree == null) { 338 CrimsonUtils.printError("Invalid tree: " + id); 339 return; 340 } 341 342 Root.runCommand("tree2wal(\"" + id + "\")"); 343 } 344 345 /** This will display the tree statistics. */ 346 public static void statsTree(String id) { 347 // if no database connection then error 348 if (! Database.isOpen()) { 349 CrimsonUtils.printError("Must open a database before viewing tree information."); 350 return; 351 } 352 353 // if no ID, then use treeSelector() to get tree 354 if (CrimsonUtils.isEmpty(id)) { 355 id = treeSelector(); 356 357 // if ID still empty then quit 358 if (CrimsonUtils.isEmpty(id)) return; 359 } 360 361 Tree tree = ObjectHandles.getTree(id); 362 // make sure tree exists 363 if (tree == null) { 364 CrimsonUtils.printError("Invalid tree: " + id); 365 return; 366 } 367 368 JOptionPane.showMessageDialog(null, tree.toString(), "Tree Statistics", 369 JOptionPane.INFORMATION_MESSAGE); 370 } 371 372 /** This will display the tree's newick string. */ 373 public static void newickTree(String id) { 374 // if no database connection then error 375 if (! Database.isOpen()) { 376 CrimsonUtils.printError("Must open a database before viewing tree information."); 377 return; 378 } 379 380 // if no ID, then use treeSelector() to get tree 381 if (CrimsonUtils.isEmpty(id)) { 382 id = treeSelector(); 383 384 // if ID still empty then quit 385 if (CrimsonUtils.isEmpty(id)) return; 386 } 387 388 Tree tree = ObjectHandles.getTree(id); 389 // make sure tree exists 390 if (tree == null) { 391 CrimsonUtils.printError("Invalid tree: " + id); 392 return; 393 } 394 395 new NewickViewer(tree.getNewick()); 396 } 397 398 static class NewickViewer extends JFrame { 399 NewickViewer thisFrame; 400 401 public NewickViewer(String newick) { 402 super("Newick Viewer"); 403 404 // keep pointer to self so can 'dispose' Frame below 405 thisFrame = this; 406 407 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 408 409 JTextArea text = new JTextArea(20, 110); 410 text.setEditable(false); 411 JScrollPane textSP = new JScrollPane(text); 412 textSP.setBorder(BorderFactory.createLoweredBevelBorder()); 413 414 // split into lines of length 100 415 int numLines = newick.length() / 100; 416 for (int i = 0; i < (numLines * 100); i += 100) { 417 text.append(newick.substring(i, i + 100) + "\n"); 418 } 419 // print remaining portion of string 420 if ((newick.length() % 100) > 0) { 421 text.append(newick.substring(numLines * 100) + "\n"); 422 } 423 424 // setup close button 425 JButton closeB = new JButton("Close Newick Viewer"); 426 closeB.addActionListener(new ActionListener() { 427 public void actionPerformed(ActionEvent e) { 428 thisFrame.dispose(); 429 } 430 }); 431 432 getContentPane().setLayout(new BorderLayout()); 433 getContentPane().add(textSP, BorderLayout.CENTER); 434 getContentPane().add(closeB, BorderLayout.SOUTH); 435 pack(); 436 437 // set the default window size 438 setSize(800, 900); 439 440 // display the window 441 setVisible(true); 442 } 443 } // NewickViewer 444 445 } // TreeUtils.java