001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2013-01-23 22:17:38 +0000 (Wed, 23 Jan 2013) $' 007 * '$Revision: 31362 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.gui; 031 032import java.awt.BorderLayout; 033import java.awt.Dimension; 034import java.awt.event.MouseListener; 035import java.util.List; 036 037import javax.swing.JLabel; 038import javax.swing.JPanel; 039import javax.swing.JScrollPane; 040import javax.swing.JTree; 041import javax.swing.event.TreeModelListener; 042import javax.swing.event.TreeSelectionListener; 043import javax.swing.tree.TreeModel; 044import javax.swing.tree.TreePath; 045import javax.swing.tree.TreeSelectionModel; 046 047import org.apache.commons.logging.Log; 048import org.apache.commons.logging.LogFactory; 049import org.kepler.gui.popups.LibraryPopupListener; 050import org.kepler.moml.DownloadableKAREntityLibrary; 051import org.kepler.moml.FolderEntityLibrary; 052import org.kepler.moml.KAREntityLibrary; 053import org.kepler.moml.KARErrorEntityLibrary; 054import org.kepler.moml.OntologyEntityLibrary; 055import org.kepler.moml.RemoteKARErrorEntityLibrary; 056import org.kepler.moml.RemoteRepositoryEntityLibrary; 057import org.kepler.moml.SearchEntityLibrary; 058import org.kepler.objectmanager.library.LibraryManager; 059import org.kepler.util.StaticResources; 060 061import ptolemy.kernel.ComponentEntity; 062import ptolemy.kernel.CompositeEntity; 063import ptolemy.kernel.util.Attribute; 064import ptolemy.kernel.util.IllegalActionException; 065import ptolemy.kernel.util.NameDuplicationException; 066import ptolemy.kernel.util.NamedObj; 067import ptolemy.kernel.util.StringAttribute; 068import ptolemy.kernel.util.Workspace; 069import ptolemy.moml.EntityLibrary; 070import ptolemy.vergil.tree.PTree; 071import ptolemy.vergil.tree.VisibleTreeModel; 072 073/** 074 * This class builds the search results by traversing the tree and trimming any 075 * sub-nodes that do not have a result in them. This leaves a minimum tree with 076 * only the search results present. 077 * 078 *@author Chad Berkley 079 *@author Shawn Bowers 080 *@author Aaron Schultz (... last editor ...) 081 */ 082public class ResultTreeRebuilder extends LibrarySearchResultPane { 083 private static final Log log = LogFactory.getLog(ResultTreeRebuilder.class 084 .getName()); 085 private static final boolean isDebugging = log.isDebugEnabled(); 086 087 private VisibleTreeModel trimmedLibrary; 088 private PTree resultsTree; 089 private Workspace workspace; 090 private JPanel resultCounterPane; 091 private JLabel resultCounterLabel; 092 private TreeSelectionListener treeSingleSelectionlistener = null; 093 094 /** 095 * the constructor passes in the library to highlight the results in and the 096 * results to highlight. if results is null, the tree is built fully 097 * collapsed with no highlights. 098 * 099 *@param library 100 * the library to highlight the results in 101 *@param results 102 * the results to highlight 103 *@exception IllegalActionException 104 * Description of the Exception 105 */ 106 public ResultTreeRebuilder(PTree library, LibrarySearchResults results) 107 throws IllegalActionException { 108 super(library, results); 109 this.workspace = ((CompositeEntity) library.getModel().getRoot()) 110 .workspace(); 111 } 112 113 /** Update the tree to display only a specific root. */ 114 public void update(EntityLibrary root) throws IllegalActionException { 115 if (isDebugging) { 116 log.debug("update(" + root.getName() + " " 117 + root.getClass().getName() + ")"); 118 } 119 this.removeAll(); 120 121 trimmedLibrary = new VisibleTreeModel(root); 122 123 AnnotatedPTree rTree = new AnnotatedPTree(trimmedLibrary, this); 124 MouseListener mListener = new LibraryPopupListener(rTree); 125 rTree.setMouseListener(mListener); 126 if(treeSingleSelectionlistener != null) 127 { 128 rTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 129 rTree.addTreeSelectionListener(treeSingleSelectionlistener); 130 } 131 rTree.initAnotatedPTree(); 132 resultsTree = rTree; 133 134 JScrollPane newpane = new JScrollPane(resultsTree); 135 newpane.setPreferredSize(new Dimension(200, 200)); 136 this.add(newpane, BorderLayout.CENTER); 137 138 // add the search results counter stuff 139 resultCounterPane = new JPanel(); 140 resultCounterPane.setBackground(TabManager.BGCOLOR); 141 resultCounterPane.setLayout(new BorderLayout()); 142 resultCounterLabel = new JLabel(""); 143 resultCounterPane.removeAll(); 144 resultCounterPane.add(resultCounterLabel, BorderLayout.CENTER); 145 this.add(resultCounterPane, BorderLayout.SOUTH); 146 147 this.repaint(); 148 this.validate(); 149 150 } 151 152 153 /** 154 * Set the tree selection listener (single selection mode will be set as well) 155 * @param listener 156 * the listener will be set 157 */ 158 public void setSingleTreeSelectionListener(TreeSelectionListener listener) 159 { 160 this.treeSingleSelectionlistener = listener; 161 } 162 163 /** 164 * Get the result tree 165 * @return 166 */ 167 public JTree getResultTree() 168 { 169 return resultsTree; 170 } 171 172 /** 173 * this method allows the search results to be updated in the panel 174 * 175 *@param results 176 * the results to update to 177 *@exception IllegalActionException 178 * Description of the Exception 179 */ 180 public void update(LibrarySearchResults results) 181 throws IllegalActionException { 182 if (isDebugging) { 183 log.debug("update(" + results + ")"); 184 } 185 this.results = results; 186 int resultcount; 187 if (results == null || results.size() == 0) { 188 resultcount = 0; 189 } else { 190 resultcount = results.size(); 191 } 192 193 this.removeAll(); 194 195 // add the results if there are any 196 if (resultcount > 0) { 197 // add the results tree. 198 EntityLibrary newRoot = new SearchEntityLibrary(workspace); 199 200 try { 201 newRoot.setName( 202 StaticResources.getDisplayString("components.searchResults", "Search Results")); 203 } catch (IllegalActionException iae) { 204 throw iae; 205 } catch (NameDuplicationException nde) { 206 throw new IllegalActionException("name duplication exception: " 207 + nde); 208 } 209 buildResultTree(newRoot); 210 trimmedLibrary = new VisibleTreeModel(newRoot); 211 212 AnnotatedPTree rTree = new AnnotatedPTree(trimmedLibrary, this); 213 MouseListener mListener = new LibraryPopupListener(rTree); 214 rTree.setMouseListener(mListener); 215 if(treeSingleSelectionlistener != null) 216 { 217 rTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); 218 rTree.addTreeSelectionListener(treeSingleSelectionlistener); 219 } 220 rTree.initAnotatedPTree(); 221 resultsTree = rTree; 222 rTree.setShowsRootHandles(false); 223 224 JScrollPane newpane = new JScrollPane(resultsTree); 225 newpane.setPreferredSize(new Dimension(200, 200)); 226 this.add(newpane, BorderLayout.CENTER); 227 expandAll(resultsTree); 228 } else { 229 // if there are no results, just add the library back 230 if (library.getModel() != null) { 231 library.setModel(new NoRemoteComponentsTreeModel(library.getModel())); 232 } 233 this.add(new JScrollPane(library), BorderLayout.CENTER); 234 } 235 236 // add the search results counter stuff 237 resultCounterPane = new JPanel(); 238 resultCounterPane.setBackground(TabManager.BGCOLOR); 239 resultCounterPane.setLayout(new BorderLayout()); 240 resultCounterLabel = new JLabel( 241 resultcount 242 + " " 243 + StaticResources.getDisplayString("components.resultsFound", "results found.")); 244 resultCounterPane.removeAll(); 245 resultCounterPane.add(resultCounterLabel, BorderLayout.CENTER); 246 this.add(resultCounterPane, BorderLayout.SOUTH); 247 248 this.repaint(); 249 this.validate(); 250 } 251 252 /** 253 * clears the tree of any items contained by newRoot 254 * 255 *@exception IllegalActionException 256 * Description of the Exception 257 * 258 public void clearTree() throws IllegalActionException { 259 if (newRoot != null && newRoot.entityList() != null) { 260 List children = newRoot.entityList(); 261 for (Object child : children ) { 262 try { 263 if (child instanceof ComponentEntity) { 264 ComponentEntity childCE = (ComponentEntity) child; 265 childCE.setContainer(null); 266 } else if (child instanceof Attribute) { 267 Attribute childAtt = (Attribute) child; 268 childAtt.setContainer(null); 269 } else { 270 log.error("Unhandled library type: " + child.getClass()); 271 } 272 } catch (Exception e) { 273 // do nothing...just go onto the next node 274 } 275 } 276 } 277 }*/ 278 279 /* 280 * build the result tree 281 */ 282 /** 283 * Description of the Method 284 * 285 *@exception IllegalActionException 286 * Description of the Exception 287 */ 288 private void buildResultTree(EntityLibrary newRoot) throws IllegalActionException { 289 if (isDebugging) 290 log.debug("buildResultTree()"); 291 292 // iterate over each treepath in results 293 for (int i = 0; i < results.size(); i++) { 294 295 try { 296 297 TreePath currentPath = results.getTreePath(i); 298 if (isDebugging) 299 log.debug(currentPath); 300 301 EntityLibrary treeCurrent = newRoot; 302 for (int j = 1; j < currentPath.getPathCount(); j++) { 303 304 NamedObj pathCurrent = (NamedObj) currentPath.getPathComponent(j); 305 306 if (pathCurrent instanceof EntityLibrary) { 307 308 List<EntityLibrary> children = treeCurrent.entityList(EntityLibrary.class); 309 310 boolean alreadyThere = false; 311 for (EntityLibrary child : children) { 312 if (isDebugging) log.debug(child.getName()); 313 if (child.getName().equals(pathCurrent.getName())) { 314 // this EntityLibrary is already there 315 treeCurrent = child; 316 alreadyThere = true; 317 break; 318 } 319 } 320 if (!alreadyThere) { 321 // create it 322 EntityLibrary newEntity = copyEntityLibrary((EntityLibrary) pathCurrent); 323 setContainer(treeCurrent,newEntity); 324 treeCurrent = newEntity; 325 } 326 327 } else { 328 List<NamedObj> children = treeCurrent.entityList(NamedObj.class); 329 330 boolean alreadyThere = false; 331 for (NamedObj child : children) { 332 if (child.getName().equals(pathCurrent.getName())) { 333 // this NamedObj is already there 334 alreadyThere = true; 335 break; 336 } 337 } 338 if (!alreadyThere) { 339 // create it 340 NamedObj newEntity = cloneEntity((NamedObj)pathCurrent); 341 setContainer(treeCurrent,newEntity); 342 // Leaf node, all done 343 break; 344 } 345 } 346 } 347 348 } catch (IllegalActionException iae) { 349 throw new IllegalActionException( 350 "cannot build search result tree: " + iae); 351 } catch (NameDuplicationException nde) { 352 log.error("EXCEPTION CAUGHT: " + nde.getMessage()); 353 } catch (Exception e) { 354 log.error("EXCEPTION CAUGHT: " + e.getMessage()); 355 } 356 } // end for loop 357 } 358 359 /** 360 * puts entity in container 361 * 362 *@param container 363 * The new container value 364 *@param entity 365 * The new container value 366 *@exception NameDuplicationException 367 * Description of the Exception 368 *@exception IllegalActionException 369 * Description of the Exception 370 */ 371 private static void setContainer(CompositeEntity container, NamedObj entity) 372 throws NameDuplicationException, IllegalActionException { 373 if (entity instanceof Attribute) { 374 ((Attribute) entity).setContainer(container); 375 } else if (entity instanceof ComponentEntity) { 376 ((ComponentEntity) entity).setContainer(container); 377 } 378 } 379 380 /** 381 * 382 * 383 *@param entity 384 * Description of the Parameter 385 *@return Description of the Return Value 386 *@exception IllegalActionException 387 * Description of the Exception 388 */ 389 private EntityLibrary copyEntityLibrary(EntityLibrary entity) 390 throws IllegalActionException { 391 if (entity == null) 392 return null; 393 try { 394 if (isDebugging) 395 log.debug("copyCompositeEntity(" + entity.getName() + ")"); 396 if (isDebugging) 397 log.debug(entity.getClass().getName()); 398 EntityLibrary el = null; 399 400 if (entity instanceof OntologyEntityLibrary) { 401 el = new OntologyEntityLibrary(new CompositeEntity( 402 this.workspace), entity.getName()); 403 } else if (entity instanceof KAREntityLibrary) { 404 el = new KAREntityLibrary(new CompositeEntity( 405 this.workspace), entity.getName()); 406 } else if (entity instanceof FolderEntityLibrary) { 407 el = new FolderEntityLibrary(new CompositeEntity( 408 this.workspace), entity.getName()); 409 } else if (entity instanceof DownloadableKAREntityLibrary) { 410 el = new DownloadableKAREntityLibrary(new CompositeEntity( 411 this.workspace), (DownloadableKAREntityLibrary) entity); 412 } else if (entity instanceof RemoteRepositoryEntityLibrary) { 413 el = new RemoteRepositoryEntityLibrary(new CompositeEntity( 414 this.workspace), entity.getName()); 415 } else if (entity instanceof KARErrorEntityLibrary) { 416 el = new KARErrorEntityLibrary(new CompositeEntity( 417 this.workspace), entity.getName()); 418 } else if (entity instanceof RemoteKARErrorEntityLibrary) { 419 el = new RemoteKARErrorEntityLibrary(new CompositeEntity( 420 this.workspace), entity.getName()); 421 ((RemoteKARErrorEntityLibrary) el).setKarXml(((RemoteKARErrorEntityLibrary) entity).getKarXml()); 422 } else { 423 el = new EntityLibrary(new CompositeEntity(this.workspace), 424 entity.getName()); 425 } 426 if (el == null) { 427 log.warn("Unrecognized Composite Entity"); 428 } else { 429 430 // set the display name to be the source's display name. 431 // the display name may be different than the name returned 432 // by getName(), e.g., the name may end in ",kar" but the 433 // display name ends in ".kar". 434 el.setDisplayName(entity.getDisplayName()); 435 436 int liid = LibraryManager.getLiidFor(entity); 437 StringAttribute liidSA = new StringAttribute(el, 438 LibraryManager.LIID_LABEL); 439 liidSA.setExpression("" + liid); 440 if (isDebugging) 441 log.debug(el.getClass().getName()); 442 } 443 return el; 444 } catch (IllegalActionException iae) { 445 throw new IllegalActionException("cannot copy composite entity: " 446 + iae); 447 } catch (NameDuplicationException nde) { 448 throw new IllegalActionException( 449 "cannot set container because the name " 450 + "already exists: " + nde); 451 } 452 } 453 454 /** 455 * clone the entity into the new workspace. 456 * 457 *@param entity 458 * Description of the Parameter 459 *@return Description of the Return Value 460 *@exception IllegalActionException 461 * Description of the Exception 462 */ 463 // private ComponentEntity cloneEntity(ComponentEntity entity) throws 464 // IllegalActionException { 465 private NamedObj cloneEntity(NamedObj entity) throws IllegalActionException { 466 try { 467 return (NamedObj) entity.clone(this.workspace); 468 } catch (java.lang.CloneNotSupportedException cnse) { 469 throw new IllegalActionException("clone not supported: " + cnse); 470 } 471 } 472 473 /** 474 * A factory that creates the searcher to search the library 475 * 476 *@author berkley 477 *@since February 17, 2005 478 */ 479 public static class Factory extends LibrarySearchResultPaneFactory { 480 /** 481 * Create an factory with the given name and container. 482 * 483 *@param container 484 * The container. 485 *@param name 486 * The name of the entity. 487 *@exception IllegalActionException 488 * If the container is incompatible with this attribute. 489 *@exception NameDuplicationException 490 * If the name coincides with an attribute already in the 491 * container. 492 */ 493 public Factory(NamedObj container, String name) 494 throws IllegalActionException, NameDuplicationException { 495 super(container, name); 496 } 497 498 /** 499 * creates a ResultTreeBuilder and returns it. 500 * 501 *@param library 502 * Description of the Parameter 503 *@param results 504 * Description of the Parameter 505 *@return A new LibraryPane that displays the library 506 *@exception IllegalActionException 507 * Description of the Exception 508 */ 509 public LibrarySearchResultPane createLibrarySearchResultPane( 510 PTree library, LibrarySearchResults results) 511 throws IllegalActionException { 512 return new ResultTreeRebuilder(library, results); 513 } 514 } 515 516 private class NoRemoteComponentsTreeModel implements TreeModel { 517 518 private boolean isRemoteComponentsNode(Object node) { 519 // TODO: A more robust test would be good here. Probably just make 520 // a subclass of EntityLibrary used only for the 521 // "Remote Components" node and then test with instanceof. 522 return "ptolemy.moml.EntityLibrary {.kepler actor library.Remote Components}".equals(node.toString()); 523 } 524 525 public NoRemoteComponentsTreeModel(TreeModel model) { 526 this.model = model; 527 for (int i = 0; i < model.getChildCount(model.getRoot()); i++) { 528 Object node = model.getChild(model.getRoot(), i); 529 if (isRemoteComponentsNode(node)) { 530 this.remoteComponentsNode = node; 531 this.remoteComponentsNodeIndex = i; 532 533 } 534 } 535 } 536 537 public Object getRoot() { 538 return model.getRoot(); 539 } 540 541 public Object getChild(Object parent, int index) { 542 return model.getChild(parent, index); 543 } 544 545 public int getChildCount(Object parent) { 546 if (parent == getRoot() && remoteComponentsNode != null) { 547 // Don't include the Remote Components 548 return model.getChildCount(parent) - 1; 549 } 550 else { 551 return model.getChildCount(parent); 552 } 553 } 554 555 public boolean isLeaf(Object node) { 556 return model.isLeaf(node); 557 } 558 559 public void valueForPathChanged(TreePath path, Object newValue) { 560 model.valueForPathChanged(path, newValue); 561 } 562 563 public int getIndexOfChild(Object parent, Object child) { 564 if (parent != getRoot() || remoteComponentsNode == null) { 565 return model.getIndexOfChild(parent, child); 566 } 567 // No index translation required if index < remote component index 568 int index = model.getIndexOfChild(parent, child); 569 if (index > this.remoteComponentsNodeIndex) { 570 return index - 1; 571 } 572 else if (index == this.remoteComponentsNodeIndex) { 573 System.out.println("Shouldn't happen!"); 574 return 0; 575 } 576 else { 577 return index; 578 } 579 } 580 581 public void addTreeModelListener(TreeModelListener l) { 582 model.addTreeModelListener(l); 583 } 584 585 public void removeTreeModelListener(TreeModelListener l) { 586 model.removeTreeModelListener(l); 587 } 588 589 private TreeModel model; 590 private Object remoteComponentsNode = null; 591 private int remoteComponentsNodeIndex = -1; 592 } 593}