001/* 002 * Copyright (c) 2004-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2013-01-18 03:25:48 +0000 (Fri, 18 Jan 2013) $' 007 * '$Revision: 31348 $' 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.objectmanager.repository; 031 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.Iterator; 037import java.util.List; 038import java.util.Map; 039import java.util.Vector; 040 041import javax.swing.JTree; 042import javax.swing.tree.TreePath; 043 044import org.apache.commons.logging.Log; 045import org.apache.commons.logging.LogFactory; 046import org.kepler.authentication.AuthenticationException; 047import org.kepler.gui.LibrarySearchPane; 048import org.kepler.gui.LibrarySearchResults; 049import org.kepler.gui.LibrarySearcher; 050import org.kepler.gui.RepositorySearcher; 051import org.kepler.kar.ModuleDependencyUtil; 052import org.kepler.kar.karxml.KarXml; 053import org.kepler.moml.DownloadableKAREntityLibrary; 054import org.kepler.moml.RemoteKARErrorEntityLibrary; 055import org.kepler.moml.RemoteRepositoryEntityLibrary; 056import org.kepler.objectmanager.cache.ActorCacheObject; 057import org.kepler.objectmanager.cache.CacheNamedObj; 058import org.kepler.objectmanager.cache.CacheObject; 059import org.kepler.sms.NamedOntClass; 060import org.kepler.sms.OntologyCatalog; 061 062import ptolemy.kernel.ComponentEntity; 063import ptolemy.kernel.CompositeEntity; 064import ptolemy.kernel.Entity; 065import ptolemy.kernel.util.ConfigurableAttribute; 066import ptolemy.kernel.util.IllegalActionException; 067import ptolemy.kernel.util.NameDuplicationException; 068import ptolemy.kernel.util.StringAttribute; 069import ptolemy.kernel.util.Workspace; 070import ptolemy.moml.EntityLibrary; 071 072/** 073 * this abstract class is should be extended by all classes that provide a 074 * search engine for the actory library. Any local variables in the extending 075 * classes should be initialized in the init method because it is called from 076 * the constructor of this class. 077 * 078 *@author berkley 079 *@since November the ninth one thousand times two plus six 080 */ 081public class EcogridRepositoryLibrarySearcher extends LibrarySearcher implements RepositorySearcher { 082 private static final Log log = LogFactory 083 .getLog(EcogridRepositoryLibrarySearcher.class.getName()); 084 private static final boolean isDebugging = log.isDebugEnabled(); 085 086 // the name of the repository 087 private String repositoryName = "defaultRepository"; // the default name. 088 // this can get reset in the constructor 089 090 // the repository 091 private Repository repository; 092 private boolean skipOntology = false; 093 094 /** 095 * constructor 096 * 097 *@param library 098 * Description of the Parameter 099 *@param searchPane 100 * Description of the Parameter 101 */ 102 public EcogridRepositoryLibrarySearcher(JTree library, 103 LibrarySearchPane searchPane, String reposName) 104 throws IllegalActionException { 105 super(library, searchPane); 106 try { 107 if (reposName != null) { 108 repositoryName = reposName; 109 } 110 if (isDebugging) { 111 log.debug("repositoryName: " + repositoryName); 112 } 113 repository = RepositoryManager.getInstance().getRepository( 114 repositoryName); 115 if (isDebugging) { 116 log.debug(repository.getClass()); 117 } 118 } catch (Exception e) { 119 e.printStackTrace(); 120 throw new IllegalActionException( 121 "Could not get an instance of the " 122 + "repository manager: " + e.getMessage()); 123 } 124 125 } 126 127 /** 128 * search a component hierarchy for a specific component in a specific 129 * component returns null if the component is not found 130 */ 131 private CompositeEntity findEntity(String name, CompositeEntity entity) { 132 if (entity.getName().trim().equals(name.trim())) { 133 return entity; 134 } else { 135 Iterator pathItt = entity.entityList().iterator(); 136 while (pathItt.hasNext()) { 137 Entity nextEntity = (Entity) pathItt.next(); 138 if (nextEntity instanceof CompositeEntity) { 139 CompositeEntity newEntity = findEntity(name, 140 (CompositeEntity) nextEntity); 141 if (newEntity != null) { 142 return newEntity; 143 } 144 } 145 } 146 } 147 return null; 148 } 149 150 /** 151 * build a path from the ontology tree from a given NamedOntClass 152 */ 153 private Object[] getTreePathObjectArray(NamedOntClass ontClass, 154 Vector<String> v) { 155 Iterator<NamedOntClass> itt = ontClass.getNamedSuperClasses(false); 156 Object[] o = null; 157 if (!itt.hasNext()) { 158 v.addElement(ontClass.getName()); 159 o = new Object[v.size()]; 160 for (int i = 0; i < v.size(); i++) { 161 o[i] = v.elementAt(i); 162 } 163 } else { 164 while (itt.hasNext()) { 165 NamedOntClass supClass = itt.next(); 166 v.addElement(ontClass.getName()); 167 return getTreePathObjectArray(supClass, v); 168 } 169 } 170 171 // the array is backwards so it needs to be flipped 172 Object[] result = new Object[o.length]; 173 for (int i = 0; i < o.length; i++) { 174 result[(result.length - 1) - i] = o[i]; 175 } 176 177 return result; 178 } 179 180 /** 181 * provides any initialization needed prior to searching. It is called when 182 * the class is constructed. Note that any local variables of extending 183 * classes should be initialized in init since it is called be the 184 * constructor of the super class (LibrarySearcher). 185 */ 186 @Override 187 protected void init() { 188 189 } 190 191 /** 192 * If we need to skip ontology display 193 */ 194 public void setSkipOntology(boolean skipOntology) 195 { 196 this.skipOntology = skipOntology; 197 } 198 199 /** 200 * search for value in the library 201 * 202 *@param value 203 * the value to search for 204 *@return Description of the Return Value 205 * @throws RepositoryException 206 * @throws AuthenticationException 207 */ 208 @Override 209 public LibrarySearchResults search(String value, boolean authenticate) 210 throws IllegalActionException, RepositoryException, AuthenticationException { 211 212 try { 213 _results = new LibrarySearchResults(); 214 215 TreePath path = _library.getPathForRow(0); // get the container 216 Object[] pathComps = path.getPath(); 217 EntityLibrary topLevel = (EntityLibrary) pathComps[0]; 218 EntityLibrary remoteLevel = null; 219 try { 220 remoteLevel = new EntityLibrary(topLevel, "Remote Components"); 221 } catch (NameDuplicationException nde) { 222 Iterator itt = topLevel.entityList().iterator(); 223 while (itt.hasNext()) { 224 CompositeEntity entity = (CompositeEntity) itt.next(); 225 if (entity.getName().equals("Remote Components")) { 226 remoteLevel = (EntityLibrary) entity; 227 remoteLevel.setContainer(null); 228 remoteLevel = new EntityLibrary(topLevel, 229 "Remote Components"); 230 } 231 } 232 } 233 // setup the remote components part of the tree 234 Object[] o = new Object[2]; 235 o[0] = topLevel; 236 o[1] = remoteLevel; 237 238 _results.add(new TreePath(o)); 239 240 // get the results from the repository 241 OntologyCatalog ontCatalog = OntologyCatalog.instance(); 242 // search the repository 243 //System.out.println("EcogridRepositoryLibrarySearcher search('" + value + "')"); 244 Iterator repoResults = repository.search(value, authenticate); 245 Iterator<EcogridRepositoryResults> castRepoResultsIterator = (Iterator<EcogridRepositoryResults>) repoResults; 246 List<EcogridRepositoryResults> repoResultsList = iteratorToList(castRepoResultsIterator); 247 //System.out.println("EcogridRepositoryLibrarySearcher found " + repoResultsList.size() + " results"); 248 List<List<EcogridRepositoryResults>> groupedResults = groupResultsByIndex(repoResultsList); 249 //System.out.println("having " + groupedResults.size() + " groups"); 250 // System.out.println("Matched KARs:"); 251 // for (List<EcogridRepositoryResults> resultGroup : groupedResults) { 252 // System.out.println("* " + resultGroup.get(0).getKarEntry().getParent().getId()); 253 // } 254 if (repoResults != null) { 255 EntityLibrary karLevel = null; 256 EntityLibrary repositoryLevel = null; 257 for (List<EcogridRepositoryResults> repoResultsGroup : groupedResults) { 258 //System.out.println("A group has "+repoResultsGroup.size()+" EcogridResult"); 259 boolean isDone = false; 260 for (EcogridRepositoryResults repoResult : repoResultsGroup) { 261 if(!isDone){ 262 karLevel = createKarLevel(remoteLevel, repoResult); 263 // Get the remote repository level 264 repositoryLevel= getRepositoryLevel(remoteLevel, repoResult.getKarEntry().getParent()); 265 isDone = true; 266 } 267 268 // get each result from the repository 269 CacheObject co = repoResult.getCacheObject(); 270 ActorCacheObject aco = (ActorCacheObject) co; 271 KarXml.KarEntry karEntry = repoResult.getKarEntry(); 272 273 Object leafObject; 274 275 if (co == null) { 276 // Not an actor 277 String name = repoResult.getName(); 278 if (name == null) { 279 name = "unnamed"; 280 } 281 282 NondraggableTreeItem nti = new NondraggableTreeItem(name); 283 if(karEntry.isWorkflow()) 284 { 285 nti.setWorkflowLSID(karEntry.getLsid()); 286 nti.setWorkflowName(karEntry.getWorkflowName()); 287 } 288 StringAttribute alternateGetPopupActionAttribute = new StringAttribute(nti, "_alternateGetPopupAction"); 289 alternateGetPopupActionAttribute.setExpression(RepositoryPopup.class.getName()); 290 StringAttribute notDraggableAttribute = new StringAttribute(nti, "_notDraggable"); 291 notDraggableAttribute.setExpression("true"); // Not strictly needed, but makes reading the MOML a little nicer. 292 293 //FIXME hardcode to add-on module 294 if ("org.kepler.reporting.roml.ReportLayout".equals(repoResult.getKarEntry().getType())) { 295 ConfigurableAttribute thumbnailAttribute = new ConfigurableAttribute(nti, "_thumbnailRasterIcon"); 296 thumbnailAttribute.setExpression("/actorthumbs/basic-report-sm.gif"); 297 } 298 leafObject = nti; 299 } else { 300 // An actor 301 CacheNamedObj cno = new CacheNamedObj(new CompositeEntity(new Workspace()), co); 302 if(karEntry.isWorkflow()) 303 { 304 cno.setWorkflowLSID(karEntry.getLsid()); 305 cno.setWorkflowName(karEntry.getWorkflowName()); 306 } 307 StringAttribute entityIdAttribute = new StringAttribute(cno, "entityId"); 308 entityIdAttribute.setExpression(aco.getLSID().toString()); 309 StringAttribute alternateGetMomlActionAttribute = new StringAttribute(cno, "_alternateGetMomlAction"); 310 alternateGetMomlActionAttribute.setExpression(AlternateGetMoml.class.getName()); 311 StringAttribute alternateGetPopupActionAttribute = new StringAttribute(cno, "_alternateGetPopupAction"); 312 alternateGetPopupActionAttribute.setExpression(RepositoryPopup.class.getName()); 313 314 ConfigurableAttribute thumbnailAttribute = new ConfigurableAttribute(cno, "_thumbnailRasterIcon"); 315 thumbnailAttribute.setExpression("/actorthumbs/basic-actor-sm.gif"); 316 317 leafObject = cno; 318 } 319 320 // find the semantic types and loop through them to place 321 // the results 322 if(!skipOntology){ 323 Vector<String> semTypes = repoResult.getSemanticTypes(); 324 for (int k = 0; k < semTypes.size(); k++) { 325 String semType = (String) semTypes.elementAt(k); 326 // find the class, search only the library ontologies 327 NamedOntClass ontClass = ontCatalog.getNamedOntClass( 328 semType, true); 329 330 if (ontClass == null) { // skip this component if it 331 // doesn't have a known class 332 continue; 333 } 334 335 // get the tree path that goes with this semantic type 336 Object[] treePathArray = getTreePathObjectArray( 337 ontClass, new Vector()); 338 339 // add the tree path to the topLevel and remoteLevel 340 // objects to make the fullTreePath 341 Object[] fullTreePath = new Object[treePathArray.length + 3]; 342 fullTreePath[0] = topLevel; 343 fullTreePath[1] = remoteLevel; 344 for (int i = 2; i < fullTreePath.length - 1; i++) { 345 // put the treepath together to be added to the 346 // results vector this just adds EntityLibraries 347 try { 348 // adding a space to the treePathArray content 349 // is a hack to keep 350 // the tree from stealing results from the 351 // remote part and putting 352 // them back into the local part. I'm not sure 353 // why it does this 354 // but keeping the names unique seems to be the 355 // only way to 356 // prevent results seepage. 357 treePathArray[i - 2] = (String) treePathArray[i - 2] 358 + " "; 359 fullTreePath[i] = new EntityLibrary( 360 (CompositeEntity) fullTreePath[i - 1], 361 (String) treePathArray[i - 2]); 362 } catch(NameDuplicationException nde) { 363 // if we get a NDE, we need to search the 364 // existing tree for the 365 // correct parent 366 fullTreePath[i] = findEntity( 367 (String) treePathArray[i - 2], 368 (CompositeEntity) fullTreePath[i - 1]); 369 if (fullTreePath[i] == null) { 370 System.out 371 .println("ERROR: no path found for fullTreePath[" 372 + i + "]"); 373 } 374 } 375 } 376 377 fullTreePath[fullTreePath.length - 1] = leafObject; 378 379 _results.add(new TreePath(fullTreePath)); 380 } 381 } 382 383 384 Object[] containmentRepresentation = getContainmentRepresentation(topLevel, remoteLevel, 385 repositoryLevel, karLevel, leafObject); 386 if (containmentRepresentation == null) { 387 log.warn("Could not generate containment representation: " + repoResult.getName()); 388 } 389 else { 390 _results.add(new TreePath(containmentRepresentation)); 391 } 392 } 393 } 394 } 395 396 if (_results.size() == 1) {// no results were added, so remove the 397 // Remote Components result tree 398 _results = new LibrarySearchResults(); 399 } 400 401 return _results; 402 } catch (NameDuplicationException nde) { 403 nde.printStackTrace(); 404 throw new IllegalActionException( 405 "Error building remote repository " + "search results: " 406 + nde.getMessage()); 407 } 408 } 409 410 411 412 private Object[] getContainmentRepresentation(EntityLibrary topLevel, EntityLibrary remoteLevel, EntityLibrary repositoryLevel, 413 EntityLibrary karLevel, Object leafObject) { 414 415 if (repositoryLevel != null) { 416 Object[] newPath = new Object[5]; 417 newPath[0] = topLevel; // Copy over "Search Results" top-level 418 newPath[1] = remoteLevel; // Copy over "Remote Components" 419 newPath[2] = repositoryLevel; 420 newPath[3] = karLevel; 421 newPath[4] = leafObject; // Copy over the component itself 422 return newPath; 423 } 424 else { 425 return null; 426 } 427 } 428 429 private EntityLibrary getRepositoryLevel(EntityLibrary parent, KarXml karXml) { 430 EntityLibrary repositoryLevel = null; 431 try { 432 repositoryLevel = new RemoteRepositoryEntityLibrary(parent, karXml.getRepositoryName()); 433 } 434 catch(IllegalActionException ex) { 435 ex.printStackTrace(); 436 } 437 catch(NameDuplicationException ex) { 438 // The containment level is already there. Find it. 439 repositoryLevel = (EntityLibrary) findEntity(karXml.getRepositoryName(), (CompositeEntity) parent); 440 } 441 return repositoryLevel; 442 } 443 444 private EntityLibrary createKarLevel(EntityLibrary parentLibrary, EcogridRepositoryResults repoResult) { 445 KarXml kx = repoResult.getKarEntry().getParent(); 446 List<String> prefixes = Arrays.asList("urn:lsid:gamma.msi.ucsb.edu/OpenAuth/:", "urn:lsid:kepler-project.org/ns/:"); 447 String rawId = kx.getName(); 448 // Remove the prefix from the rawId before sanitizing. All that 449 // boilerplate just gets in the way. 450 for (String prefix : prefixes) { 451 if (rawId.startsWith(prefix)) { 452 rawId = rawId.substring(prefix.length()); 453 break; 454 } 455 } 456 String karLevelName = rawId; 457 if (!karLevelName.endsWith(".kar")) { 458 karLevelName += ".kar"; 459 } 460 EntityLibrary karLevel = null; 461 462 Vector<String> modDeps = new Vector<String>(kx.getModuleDependencies()); 463 boolean dependenciesSatisfied = ModuleDependencyUtil.checkIfModuleDependenciesSatisfied(modDeps); 464 try { 465 // replace periods in the name since they are not allowed in NamedObj 466 String sanitizedName = karLevelName.replace(".", ","); 467 if (dependenciesSatisfied) { 468 karLevel = new DownloadableKAREntityLibrary(parentLibrary, sanitizedName, kx); 469 } 470 else { 471 karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, sanitizedName); 472 ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx); 473 } 474 // karLevel = new EntityLibrary(parentLibrary, karLevelName); 475 476 // set the display name, which can have periods 477 karLevel.setDisplayName(karLevelName); 478 } 479 catch (NameDuplicationException nde) { 480 //System.out.println("name duplication exception "+nde.getMessage()); 481 try { 482 // add an appendix for the name 483 if(kx != null && kx.getLsid() != null) 484 { 485 //add docid as an appendix 486 karLevelName = karLevelName+"-(karId-"+transformLSIDtoDocid(kx.getLsid())+")"+ 487 "-(karXmlId-"+repoResult.getDocid().replace('.', ':')+")"; 488 } 489 else 490 { 491 //add random number 492 double number = Math.random(); 493 long appendix = Math.round(number); 494 karLevelName = karLevelName+"-("+appendix+")"; 495 } 496 497 // replace periods in the name since they are not allowed in NamedObj 498 String sanitizedName = karLevelName.replace(".", ","); 499 if (dependenciesSatisfied) { 500 karLevel = new DownloadableKAREntityLibrary(parentLibrary, sanitizedName, kx); 501 } 502 else { 503 karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, sanitizedName); 504 ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx); 505 } 506 // set the display name, which can have periods 507 karLevel.setDisplayName(karLevelName); 508 } 509 catch(NameDuplicationException ex) { 510 log.warn("This shouldn't happen", ex); 511 } 512 catch(IllegalActionException ex) { 513 log.error("Illegal action exception", ex); 514 } 515 /*Iterator<CompositeEntity> iterator = (Iterator<CompositeEntity>) parentLibrary.entityList().iterator(); 516 while (iterator.hasNext()) { 517 CompositeEntity entity = iterator.next(); 518 if (entity.getName().equals(karLevelName)) { 519 try { 520 karLevel = (EntityLibrary) entity; 521 karLevel.setContainer(null); 522 if (dependenciesSatisfied) { 523 karLevel = new DownloadableKAREntityLibrary(parentLibrary, karLevelName, karEntry.getParent()); 524 } 525 else { 526 karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, karLevelName); 527 ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx); 528 } 529 } 530 catch(NameDuplicationException ex) { 531 log.warn("This shouldn't happen", ex); 532 } 533 catch(IllegalActionException ex) { 534 log.error("Illegal action exception", ex); 535 } 536 } 537 }*/ 538 } 539 catch(IllegalActionException ex) { 540 log.error("Illegal action exception", ex); 541 } 542 return karLevel; 543 } 544 545 /* 546 *Transform a urn:lsid:kepler-project.org/ns/:2099:21:2 to 2099.21.2 547 */ 548 private String transformLSIDtoDocid(String lsid) { 549 String docid = null; 550 char colon = ':'; 551 int targetNumber =3; 552 if(lsid != null){ 553 int index =0; 554 for(int i=0; i<lsid.length(); i++){ 555 char character = lsid.charAt(lsid.length()-1-i); 556 if(character == colon){ 557 index = index+1; 558 if(index == targetNumber){ 559 docid = lsid.substring(lsid.length()-i); 560 } 561 } 562 } 563 } 564 return docid; 565 } 566 567 private List<List<EcogridRepositoryResults>> groupResultsByIndex(List<EcogridRepositoryResults> repoResults) { 568 Map<Integer, List<EcogridRepositoryResults>> resultsByIndex = new HashMap<Integer, List<EcogridRepositoryResults>>(); 569 for (EcogridRepositoryResults repoResult : repoResults) { 570 Integer indexValue = repoResult.getIndexValue(); 571 if (!resultsByIndex.containsKey(indexValue)) { 572 resultsByIndex.put(indexValue, new ArrayList<EcogridRepositoryResults>()); 573 } 574 resultsByIndex.get(indexValue).add(repoResult); 575 } 576 577 List<List<EcogridRepositoryResults>> results = new ArrayList<List<EcogridRepositoryResults>>(); 578 for (Integer indexValue : resultsByIndex.keySet()) { 579 if (indexValue == null) { 580 // These aren't in a single group of 'not a group'. Add them 581 // as separate lists. 582 for (EcogridRepositoryResults repoResult : resultsByIndex.get(null)) { 583 results.add(Collections.singletonList(repoResult)); 584 } 585 } 586 else { 587 results.add(resultsByIndex.get(indexValue)); 588 } 589 } 590 return results; 591 } 592 593 private <T> List<T> iteratorToList(Iterator<T> iterator) { 594 if (iterator == null) { 595 return Collections.emptyList(); 596 } 597 List<T> list = new ArrayList<T>(); 598 while (iterator.hasNext()) { 599 list.add(iterator.next()); 600 } 601 return list; 602 } 603 604 public class NondraggableTreeItem extends ComponentEntity { 605 606 private String workflowName = null; 607 private String workflowLSID = null; 608 609 public NondraggableTreeItem(String name) throws IllegalActionException, NameDuplicationException { 610 super(new CompositeEntity(new Workspace()), name.replace('.', ',')); 611 setDisplayName(name); 612 } 613 614 public String getWorkflowName(){ 615 return workflowName; 616 } 617 public void setWorkflowName(String workflowName){ 618 this.workflowName = workflowName; 619 } 620 621 public String getWorkflowLSID(){ 622 return workflowLSID; 623 } 624 625 public void setWorkflowLSID(String workflowLSID){ 626 this.workflowLSID = workflowLSID; 627 } 628 } 629}