001/** 002 * '$RCSfile$' 003 * '$Author: crawl $' 004 * '$Date: 2015-08-24 22:44:14 +0000 (Mon, 24 Aug 2015) $' 005 * '$Revision: 33630 $' 006 * 007 * For Details: 008 * http://www.kepler-project.org 009 * 010 * Copyright (c) 2012 The Regents of the 011 * University of California. All rights reserved. Permission is hereby granted, 012 * without written agreement and without license or royalty fees, to use, copy, 013 * modify, and distribute this software and its documentation for any purpose, 014 * provided that the above copyright notice and the following two paragraphs 015 * appear in all copies of this software. IN NO EVENT SHALL THE UNIVERSITY OF 016 * CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, 017 * OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS 018 * DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE 019 * POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY 020 * DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 022 * SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 023 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 024 * ENHANCEMENTS, OR MODIFICATIONS. 025 */ 026package org.kepler.loader.util; 027 028import java.io.BufferedReader; 029import java.io.BufferedWriter; 030import java.io.File; 031import java.io.FileInputStream; 032import java.io.FileOutputStream; 033import java.io.FileReader; 034import java.io.FileWriter; 035import java.io.IOException; 036import java.io.InputStream; 037import java.io.OutputStream; 038import java.lang.reflect.Field; 039import java.net.URL; 040import java.sql.Connection; 041import java.sql.ResultSet; 042import java.sql.SQLException; 043import java.sql.Statement; 044import java.util.Arrays; 045import java.util.HashMap; 046import java.util.HashSet; 047import java.util.LinkedList; 048import java.util.List; 049import java.util.Map; 050import java.util.Set; 051import java.util.Vector; 052import java.util.regex.Matcher; 053import java.util.regex.Pattern; 054 055import org.kepler.Kepler; 056import org.kepler.build.modules.Module; 057import org.kepler.build.modules.ModuleTree; 058import org.kepler.kar.KARCacheContent; 059import org.kepler.kar.KARCacheManager; 060import org.kepler.kar.KAREntry; 061import org.kepler.kar.KARManifest; 062import org.kepler.kar.KarDoclet; 063import org.kepler.kar.SuperClassPathFinderDoclet; 064import org.kepler.kar.handlers.ActorMetadataKAREntryHandler; 065import org.kepler.moml.NamedObjId; 066import org.kepler.objectmanager.ActorMetadata; 067import org.kepler.objectmanager.library.LibraryManager; 068import org.kepler.objectmanager.lsid.KeplerLSID; 069import org.kepler.sms.SemanticType; 070import org.kepler.util.sql.DatabaseFactory; 071import org.kepler.util.sql.HSQL; 072 073import com.sun.tools.javadoc.Main; 074 075import ptolemy.actor.AtomicActor; 076import ptolemy.actor.CompositeActor; 077import ptolemy.actor.Director; 078import ptolemy.actor.TypedCompositeActor; 079import ptolemy.actor.gui.ConfigurationApplication; 080import ptolemy.actor.lib.RandomSource; 081import ptolemy.actor.lib.TimedSource; 082import ptolemy.kernel.CompositeEntity; 083import ptolemy.kernel.Port; 084import ptolemy.kernel.util.IllegalActionException; 085import ptolemy.kernel.util.NamedObj; 086import ptolemy.kernel.util.Workspace; 087import ptolemy.moml.MoMLParser; 088import ptolemy.util.MessageHandler; 089import ptolemy.vergil.basic.KeplerDocumentationAttribute; 090import ptolemy.vergil.basic.export.web.WebContent; 091import util.StringUtil; 092 093/** A class to update the KAR XMLs for actors, directors, etc. 094 * 095 * @author Daniel Crawl 096 * @version $Id: UpdateActorTreeFiles.java 33630 2015-08-24 22:44:14Z crawl $ 097 */ 098public class UpdateActorTreeFiles { 099 100 /** Update the KAR XMLs for modules. 101 * 102 * @param args a list of module names 103 */ 104 public static void main(String[] args) { 105 106 // call the module initializers 107 try { 108 Kepler.initialize(); 109 } catch (Exception e) { 110 MessageHandler.error("ERROR initializing modules.", e); 111 System.exit(1); 112 } 113 114 Kepler.setOntologyIndexFile(); 115 116 _initializeCache(); 117 118 final ModuleTree tree = ModuleTree.instance(); 119 List<String> moduleNames; 120 if(args.length == 0 || (args.length == 1 && args[0].equals("undefined"))) { 121 List<Module>modules = tree.getModuleList(); 122 moduleNames = new LinkedList<String>(); 123 for(Module module : modules) { 124 moduleNames.add(module.getName()); 125 } 126 } else { 127 moduleNames = Arrays.asList(args); 128 } 129 130 for(String name : moduleNames) { 131 try { 132 updateKarXMLDocsForModule(name); 133 } catch(Exception e) { 134 MessageHandler.error("ERROR updating module " + name, e); 135 } 136 } 137 138 _shutdownCache(true); 139 } 140 141 /** */ 142 public static void updateKarXMLDocsForFile(String[] args) throws Exception { 143 144 boolean overwrite = false; 145 boolean allowMultiClasses = false; 146 List<String> names = new LinkedList<String>(); 147 for(String arg : args) { 148 if(arg.equals("-overwrite")) { 149 overwrite = true; 150 } else if(arg.equals("-allowMultiClasses")) { 151 allowMultiClasses = true; 152 } else { 153 names.add(arg); 154 } 155 } 156 157 _initializeCache(); 158 159 final ModuleTree moduleTree = ModuleTree.instance(); 160 Connection conn = null; 161 Statement st = null; 162 ResultSet result = null; 163 try { 164 conn = DatabaseFactory.getDBConnection(); 165 st = conn.createStatement(); 166 for(String name : names) { 167 System.out.println("Updating docs for " + name); 168 169 if(!name.endsWith(".xml")) { 170 System.out.println(" ERROR: do not know how to update " + name + " (does not end in .xml)"); 171 } else { 172 File xmlFile = new File(name); 173 String xmlName = xmlFile.getName(); 174 result = st.executeQuery("select kars_cached.file, kar_contents.name, reponame, classname, kar_contents.lsid " + 175 "from kars_cached, kar_contents, cachecontenttable " + 176 "where kar_contents.lsid = cachecontenttable.lsid and " + 177 "kar_contents.file = kars_cached.file and kar_contents.name = '" + xmlName + "'"); 178 } 179 if(!result.next()) { 180 System.out.println(" ERROR: could not find " + name + " in cache."); 181 continue; 182 } 183 184 String karPath = result.getString(1); 185 final String xmlName = result.getString(2); 186 final String moduleName = result.getString(3).toLowerCase(); 187 final String className = result.getString(4); 188 final String lsidStr = result.getString(5); 189 190 Module module = moduleTree.getModuleByStemName(moduleName); 191 if(module == null) { 192 throw new IllegalActionException("ERROR: module " + moduleName + " not in modules.txt"); 193 } 194 195 // the path returned is the query is something like: 196 // .../KeplerData/modules/actors/kar/CoreActors.kar 197 // the name of the kar is the directory name where to write the new xml file: 198 // .../kepler.modules/actors/resources/kar/CoreActors 199 200 final File karRepoFile = new File(karPath.replaceAll("\\.kar$", "")); 201 final String karDirName = karRepoFile.getName(); 202 final String fullXMLPath = module.getKarResourcesDir() + File.separator + karDirName; 203 204 updateDocs(className, fullXMLPath + File.separator + xmlName, 205 true, lsidStr, !overwrite, allowMultiClasses); 206 } 207 } finally { 208 try { 209 if(result != null) { 210 result.close(); 211 } 212 if(st != null) { 213 st.close(); 214 } 215 if(conn != null) { 216 conn.close(); 217 } 218 } catch(SQLException e) { 219 MessageHandler.error("ERROR closing cache database.", e); 220 } 221 } 222 223 _shutdownCache(true); 224 } 225 226 /** Update the KAR XML documentation for a module. */ 227 public static void updateKarXMLDocsForModule(String moduleName) throws Exception { 228 229 System.out.println("Updating docs for module " + moduleName); 230 231 // get the classes for the xmls 232 233 ModuleTree moduleTree = ModuleTree.instance(); 234 Module module = moduleTree.getModuleByStemName(moduleName); 235 if(module == null) { 236 throw new IllegalActionException("ERROR: module " + moduleName + " not in modules.txt"); 237 } 238 239 Connection conn = null; 240 Statement st = null; 241 ResultSet result = null; 242 try { 243 conn = DatabaseFactory.getDBConnection(); 244 st = conn.createStatement(); 245 246 // first character of repository name is upper case 247 final char[] stringArray = moduleName.toCharArray(); 248 stringArray[0] = Character.toUpperCase(stringArray[0]); 249 final String repoName = new String(stringArray); 250 251 result = st.executeQuery("select kars_cached.file, kar_contents.name, classname, kar_contents.lsid " + 252 "from kars_cached, kar_contents, cachecontenttable " + 253 "where kar_contents.lsid = cachecontenttable.lsid and " + 254 "kar_contents.file = kars_cached.file and reponame = '" + repoName + "'"); 255 256 while(result.next()) { 257 String karPath = result.getString(1); 258 final String xmlName = result.getString(2); 259 final String className = result.getString(3); 260 final String lsidStr = result.getString(4); 261 262 // the path returned is the query is something like: 263 // .../KeplerData/modules/actors/kar/CoreActors.kar 264 // the name of the kar is the directory name where to write the new xml file: 265 // .../kepler.modules/actors/resources/kar/CoreActors 266 267 final File karRepoFile = new File(karPath.replaceAll("\\.kar$", "")); 268 final String karDirName = karRepoFile.getName(); 269 final String fullXMLPath = module.getKarResourcesDir() + File.separator + karDirName; 270 271 final String outputFileName = fullXMLPath + File.separator + xmlName; 272 System.out.println("Updating docs for " + outputFileName); 273 updateDocs(className, outputFileName, false, lsidStr, true, false); 274 } 275 276 } finally { 277 try { 278 if(result != null) { 279 result.close(); 280 } 281 if(st != null) { 282 st.close(); 283 } 284 if(conn != null) { 285 conn.close(); 286 } 287 } catch(SQLException e) { 288 MessageHandler.error("ERROR closing cache database.", e); 289 } 290 } 291 } 292 293 /** Create XML files for actor KARs. 294 * @param names a java source file names or class name. 295 * @param outputFileName the name of the output file. 296 * @param printWhenChangePropName if true, print when the class field names 297 * in the docs are different from the name returned by NamedObj.getName(). 298 * @param lsidStr the lsid 299 * @param onlyCreateForMissing if true, documentation is only created if not present. 300 * @param allowMultiClasses if true, create documentation even if multiple actors 301 * use the same class, e.g., customized versions of the R actor. 302 */ 303 public static void updateDocs(String name, String outputFileName, 304 boolean printWhenChangePropName, String lsidStr, 305 boolean onlyCreateForMissing, boolean allowMultiClasses) throws Exception { 306 307 //System.out.println(" output file name = " + outputFileName); 308 309 // read the existing file 310 _parser.reset(); 311 NamedObj oldKarXML = _parser.parseFile(outputFileName); 312 final KeplerDocumentationAttribute oldDoc = (KeplerDocumentationAttribute) oldKarXML.getAttribute("KeplerDocumentation"); 313 final KeplerDocumentationAttribute tmpDoc = new KeplerDocumentationAttribute(_workspace); 314 tmpDoc.createInstanceFromExisting(oldDoc); 315 if(tmpDoc != null) { 316 String userLevelDoc = tmpDoc.getUserLevelDocumentation(); 317 if(onlyCreateForMissing && userLevelDoc != null && userLevelDoc.trim().length() > 10) { 318 System.out.println("WARNING: not updating docs for " + 319 outputFileName + " since docs already exist."); 320 return; 321 } 322 } 323 324 String fileName = _getFileName(name); 325 if(fileName == null) { 326 return; 327 } 328 329 final KeplerDocumentationAttribute newDoc = _generateDocsFromDoclet(fileName); 330 if(newDoc == null) { 331 return; 332 } 333 334 final String className = _getClassName(fileName); 335 336 if(!allowMultiClasses && _classHasMultipleDocs(className)) { 337 System.out.println("WARNING: not updating docs for " + outputFileName + " since multiple doc files found for " + className); 338 return; 339 } 340 341 //System.out.println(" class name = " + className); 342 343 String shortName = className 344 .substring(className.lastIndexOf(".") + 1); 345 346 Class<?> cls = Class.forName(className); 347 NamedObj no; 348 349 try { 350 no = ActorMetadata.createInstance(cls, 351 new Object[] { new CompositeEntity(), shortName }); 352 } catch(Exception e) { 353 System.err.println("ERROR: could not instantiate " + className); 354 return; 355 } 356 357 // convert names of parameters and ports from object field name 358 // to the name returned by NamedObj.getName() 359 _fixNames(no, newDoc, printWhenChangePropName); 360 361 if(oldDoc != null) { 362 363 /* 364 // see if we're only creating docs when they are missing 365 if(onlyCreateForMissing) { 366 // update the existing docs from the new class docs 367 oldDoc.updateFromExisting(newDoc, true); 368 } else { 369 */ 370 371 // use old entries if the javadoc ones are empty 372 newDoc.updateFromExisting(oldDoc, true); 373 374 // remove the old doc attribute, and move the new doc attribute 375 // to the same position as the old one 376 int index = oldDoc.moveToLast(); 377 oldDoc.setContainer(null); 378 newDoc.setContainer(oldKarXML); 379 newDoc.moveToIndex(index); 380 381 //} 382 } else { 383 newDoc.setContainer(oldKarXML); 384 } 385 386 String xmlStr = oldKarXML.exportMoML(); 387 String prettyXMLStr = StringUtil.prettifyXML(xmlStr, 2); 388 389 final File outputFile = new File(outputFileName); 390 final FileWriter writer = new FileWriter(outputFile); 391 writer.write(prettyXMLStr); 392 writer.close(); 393 } 394 395 /** Create XML files for a list of source files. */ 396 public static void buildXMLs(String args[]) { 397 398 _initializeCache(); 399 400 List<String> names = new LinkedList<String>(); 401 402 boolean dup = true; 403 boolean requireSemanticTypes = false; 404 405 for (String arg : args) { 406 if(arg.equals("-nodup")) { 407 System.out.println("Will not create files for actors already in cache."); 408 dup = false; 409 } else if(arg.equals("-requireSem")) { 410 System.out.println("Will not create files for actors without semantic types."); 411 requireSemanticTypes = true; 412 } else { 413 names.add(arg); 414 } 415 } 416 417 // do not generate XMLs for deprecated classes 418 KarDoclet.setGenerateForDeprecated(false); 419 420 for(String name : names) { 421 System.out.println("Building actor XML for " + name); 422 buildKARXML(name, null, null, true, true, dup, requireSemanticTypes); 423 } 424 425 _shutdownCache(true); 426 } 427 428 /** Create XML files for actor KARs. 429 * @param names a java source file names or class name. 430 * @param outputDir the output directory to write the file to. if null, 431 * then try to guess module containing the source or class name and write 432 * to module/resources/kar. if cannot guess module, write to user.dir. 433 * @param outputName the name of the output file. if null, then use the short class name. 434 * @param printWhenNameIsDifferent if true, print when the class name and name in the cache are different. 435 * @param printWhenChangePropName if true, print when the class field names in the docs are different 436 * from the name returned by NamedObj.getName(). 437 * @param duplicateExistingActor if true, create XML for actor already in cache. otherwise, 438 * do nothing. 439 * @param requireSemanticTypes if true, do not create XML for actors unless semantic types are 440 * known or can be guessed. 441 */ 442 public static void buildKARXML(String name, String outputDir, String outputName, 443 boolean printWhenNameIsDifferent, boolean printWhenChangePropName, 444 boolean duplicateExistingActor, boolean requireSemanticTypes) { 445 try { 446 447 final KARCacheManager karCacheManager = KARCacheManager.getInstance(); 448 final Vector<KARCacheContent> karCacheContents = karCacheManager.getKARCacheContents(); 449 450 String fileName = _getFileName(name); 451 if(fileName == null) { 452 return; 453 } 454 455 final String className = _getClassName(fileName); 456 457 if(className == null) { 458 System.err.println("ERROR: could not determine class name for " + fileName); 459 return; 460 } 461 462 //System.out.println("class name is = " + className); 463 464 String shortName = className 465 .substring(className.lastIndexOf(".") + 1); 466 467 Class<?> cls = Class.forName(className); 468 NamedObj no; 469 470 try { 471 no = ActorMetadata.createInstance(cls, 472 new Object[] { new CompositeEntity(), shortName }); 473 } catch(Exception e) { 474 System.err.println("ERROR: could not instantiate " + className); 475 return; 476 } 477 478 no = (NamedObj) no.clone(_workspace); 479 480 String LSIDStr = null; 481 Vector<KeplerLSID> semanticLSIDs = null; 482 483 final String parentClassName = cls.getSuperclass().getName(); 484 Vector<KeplerLSID> parentSemanticLSIDs = null; 485 486 //System.out.println(" Searching for " + className + " in Kepler actor cache."); 487 488 boolean foundActorInCache = false; 489 490 for(KARCacheContent content : karCacheContents) { 491 String cacheClassName = content.getCacheContent().getClassName(); 492 493 // make sure class name is not null. 494 // it's null for, e.g., workflow runs and report layouts 495 if(cacheClassName != null) { 496 497 if(cacheClassName.equals(className)) { 498 //System.out.println(" Found in cache."); 499 500 // get the lsid 501 KeplerLSID lsid = content.getLsid(); 502 LSIDStr = lsid.toString(); 503 foundActorInCache = true; 504 if(!duplicateExistingActor) { 505 System.out.println(" WARNING: will not duplicate actor in cache: " + name); 506 return; 507 } 508 509 // get the semantic types 510 semanticLSIDs = content.getSemanticTypes(); 511 512 String cachedName = content.getCacheContent().getName(); 513 if(!shortName.equals(cachedName)) { 514 if(printWhenNameIsDifferent) { 515 System.out.println(" using name found in cache: " + cachedName); 516 } 517 no.setName(cachedName); 518 } 519 break; 520 521 } else if(cacheClassName.equals(parentClassName)) { 522 parentSemanticLSIDs = content.getSemanticTypes(); 523 } 524 } 525 } 526 527 // generate and set the documentation 528 final KeplerDocumentationAttribute doc = _generateDocsFromDoclet(fileName); 529 if(doc == null) { 530 return; 531 } 532 533 _fixNames(no, doc, printWhenChangePropName); 534 doc.setContainer(no); 535 536 //_removeDefaultValues(no); 537 538 Set<String> semanticTypes = new HashSet<String>(); 539 540 // see if we found semantic types in the cache 541 if(semanticLSIDs == null || semanticLSIDs.isEmpty()) { 542 543 // try guessing 544 semanticTypes = _guessSemanticTypes(no); 545 for(String type : semanticTypes) { 546 System.out.println(" guessed " + type); 547 } 548 549 // see if we found semantic types of the parent class in the cache. 550 if(parentSemanticLSIDs != null) { 551 System.out.println(" WARNING: semantic types not found; using types from parent " + parentClassName); 552 semanticLSIDs = parentSemanticLSIDs; 553 } 554 } 555 556 // if we found semantic types in the cache for the class or a parent class, 557 // convert to strings. 558 if(semanticLSIDs != null) { 559 // convert from LSID to string 560 for(KeplerLSID semanticTypeLSID : semanticLSIDs) { 561 semanticTypes.add(semanticTypeLSID.toString()); 562 } 563 } 564 565 // see if we still have to semantic types 566 if(semanticTypes.isEmpty()) { 567 568 if(requireSemanticTypes) { 569 System.out.println(" WARNING: will not create XML since no semantic types for " + name); 570 return; 571 } 572 573 // add a default semantic type 574 semanticTypes.add("urn:lsid:localhost:onto:2:1#FIXME"); 575 System.out.println(" WARNING: semantic types need to be set for " + name); 576 } 577 578 // add the semantic types 579 int count = 0; 580 for(String semanticTypeStr : semanticTypes) { 581 SemanticType semanticType = new SemanticType(no, "semanticType0" + count); 582 semanticType.moveToLast(); 583 semanticType.setExpression(semanticTypeStr); 584 count++; 585 //System.out.println(" added semantic type " + semanticType.getName() + " = " + semanticType.getExpression()); 586 } 587 588 NamedObjId noId = new NamedObjId(no, NamedObjId.NAME); 589 noId.moveToFirst(); 590 591 if(LSIDStr != null) { 592 noId.setExpression(LSIDStr); 593 //System.out.println("LSID is " + LSIDStr); 594 } else { 595 596 LSIDStr = _getNextActorLSIDFromReadme(); 597 598 if(LSIDStr != null) { 599 System.out.println(" Using new LSID: " + LSIDStr); 600 noId.setExpression(LSIDStr); 601 } else { 602 System.out.println(" WARNING: the LSID needs to be set."); 603 noId.setExpression("urn:lsid:kepler-project.org:actor:999:1"); 604 } 605 } 606 607 if(outputDir == null) { 608 609 // try to find the module containing the source. 610 // NOTE: if the module is found, outputDir is set to 611 // module/resources/kar. However, the actor kar 612 // entries are always in a sub-directory of this 613 // directory. Usually there is only one sub-directory 614 // in module/resources/kar, and below we try to find it. 615 616 Module module = findModuleSrcDirForNameObj(no); 617 if(module != null) { 618 File karResourcesDir = module.getKarResourcesDir(); 619 if(!karResourcesDir.exists() && !karResourcesDir.mkdirs()) { 620 System.out.println(" WARNING: unable to mkdirs: " + karResourcesDir); 621 } else { 622 // see if there is only one directory in the kar resources dir 623 String potentialDir = null; 624 int potentialDirs = 0; 625 final File[] filesInKarResourcesDir = karResourcesDir.listFiles(); 626 for(File fileInKarResourcesDir : filesInKarResourcesDir) { 627 if(fileInKarResourcesDir.isDirectory() && !fileInKarResourcesDir.getName().startsWith(".")) { 628 potentialDirs++; 629 potentialDir = fileInKarResourcesDir.getAbsolutePath(); 630 } 631 } 632 // if there's only one sub-directory, use it 633 if(potentialDirs == 1) { 634 outputDir = potentialDir; 635 } else { 636 // otherwise use module/resources/kar and files must be moved by hand 637 outputDir = karResourcesDir.getAbsolutePath(); 638 } 639 } 640 } 641 } 642 643 if(outputDir == null) { 644 outputDir = System.getProperty("user.dir"); 645 } 646 647 // use ActorMetadata instead of NamedObj.exportMoML() to generate 648 // the XML since the XML generated by exportMoML is not complete: 649 // missing <?xml version="1.0"?> (necessary?) 650 // <entity> class attribute is actor class, not ptolemy.kernel.ComponentEntity 651 652 String xmlOutputFileName = outputDir + File.separator; 653 if(outputName == null) { 654 xmlOutputFileName += shortName + ".xml"; 655 } else { 656 xmlOutputFileName += outputName; 657 } 658 659 System.out.println(" output file is " + xmlOutputFileName); 660 661 final File xmlOutputFile = new File(xmlOutputFileName); 662 663 // if we're not duplicating existing actors, make sure the output 664 // file does not exist. 665 if(!duplicateExistingActor && xmlOutputFile.exists()) { 666 System.out.println(" not writing output file since it already exists."); 667 return; 668 } 669 670 FileWriter writer = new FileWriter(xmlOutputFile); 671 ActorMetadata actorMetadata = null; 672 673 if(actorMetadata == null) { 674 actorMetadata = new ActorMetadata(no); 675 } 676 677 writer.write(actorMetadata.toString(false, false, false)); 678 writer.close(); 679 680 if(!foundActorInCache) { 681 InputStream inputStream = null; 682 OutputStream outputStream = null; 683 try { 684 // create a MANIFEST.MF if it does not exist 685 KARManifest manifest; 686 File manifestFile = new File(outputDir, "MANIFEST.MF"); 687 if(!manifestFile.exists()) { 688 manifest = new KARManifest(); 689 } else { 690 inputStream = new FileInputStream(manifestFile); 691 manifest = new KARManifest(inputStream); 692 } 693 694 // add the new entries 695 // e.g.: 696 // Name: DecimalFormatConverter.xml 697 // type: ptolemy.kernel.ComponentEntity 698 // lsid: urn:lsid:kepler-project.org:actor:570:1 699 // handler: org.kepler.kar.handlers.ActorMetadataKAREntryHandler 700 701 String typeStr; 702 if(no instanceof AtomicActor) { 703 typeStr = "ptolemy.kernel.ComponentEntity"; 704 } else if(no instanceof CompositeActor) { 705 typeStr = "org.kepler.moml.CompositeClassEntity"; 706 } else { 707 typeStr = "org.kepler.moml.PropertyEntity"; 708 } 709 710 String xmlOutputFileBaseName = xmlOutputFile.getName(); 711 manifest.addEntryAttribute(xmlOutputFileBaseName, KAREntry.TYPE.toString(), typeStr); 712 manifest.addEntryAttribute(xmlOutputFileBaseName, KAREntry.LSID.toString(), LSIDStr); 713 manifest.addEntryAttribute(xmlOutputFileBaseName, KAREntry.HANDLER.toString(), 714 ActorMetadataKAREntryHandler.class.getName()); 715 716 717 if(inputStream != null) { 718 inputStream.close(); 719 inputStream = null; 720 } 721 722 // write the updated manifest 723 outputStream = new FileOutputStream(manifestFile); 724 manifest.write(outputStream); 725 726 } finally { 727 if(inputStream != null) { 728 inputStream.close(); 729 } 730 if(outputStream != null) { 731 outputStream.close(); 732 } 733 } 734 } 735 } catch (Exception e) { 736 MessageHandler.error("Error creating XML.", e); 737 } 738 } 739 740 /** Find the module whose source directory contains a NamedObj. 741 * Returns null if not found. 742 */ 743 public static Module findModuleSrcDirForNameObj(NamedObj namedObj) throws ClassNotFoundException { 744 745 ModuleTree tree = ModuleTree.instance(); 746 747 String className = namedObj.getClass().getName(); 748 749 // see if it's a ptolemy class 750 if(className.startsWith("ptolemy")) { 751 if(namedObj instanceof Director) { 752 return tree.getModuleByStemName("directors"); 753 } else { 754 return tree.getModuleByStemName("actors"); 755 } 756 } 757 758 String fileName = _getFileName(className); 759 if(fileName != null) { 760 for(Module module : tree.getModuleList()) { 761 if(fileName.startsWith(module.getSrc().getAbsolutePath())) { 762 return module; 763 } 764 } 765 } 766 return null; 767 } 768 769 /** Try to guess the semantic types for a NamedObj. */ 770 private static Set<String> _guessSemanticTypes(NamedObj namedObj) { 771 772 Set<String> types = new HashSet<String>(); 773 774 Matcher matcher; 775 776 final String namedObjName = namedObj.getName(); 777 final String namedObjClassName = namedObj.getClass().getName(); 778 final KeplerDocumentationAttribute doc = 779 (KeplerDocumentationAttribute) namedObj.getAttribute("KeplerDocumentation"); 780 final String userLevelDocLowerCase = doc.getUserLevelDocumentation().toLowerCase(); 781 782 if(namedObjName.contains("Array")) { 783 types.add("urn:lsid:localhost:onto:2:1#DataArrayOperation"); 784 } 785 786 matcher = _CONVERT_FROM_TO_PATTERN.matcher(namedObjName); 787 if(namedObjName.contains("Assembler") || 788 namedObjName.contains("Disassembler") || 789 namedObjName.contains("Updater") || 790 (matcher.find() && !(namedObjName.contains("String")))) { 791 types.add("urn:lsid:localhost:onto:2:1#DataStructureOperation"); 792 } 793 794 if(namedObjName.contains("String")) { 795 types.add("urn:lsid:localhost:onto:2:1#DataStringOperation"); 796 } 797 798 if(namedObjName.endsWith(("Average")) || 799 namedObjName.endsWith("Maximum") || 800 namedObjName.endsWith(("Minimum"))) { 801 types.add("urn:lsid:localhost:onto:2:1#StatisticalOperation"); 802 } 803 804 if(namedObjName.contains("Matrix")) { 805 types.add("urn:lsid:localhost:onto:2:1#MatrixOperation"); 806 } 807 808 if(namedObj instanceof RandomSource) { 809 types.add("urn:lsid:localhost:onto:2:1#RandomNumberOperation"); 810 } 811 812 if(namedObjClassName.startsWith("ptolemy.actor.lib.comm")) { 813 types.add("urn:lsid:localhost:onto:2:1#Communications"); 814 } 815 816 if(namedObjClassName.startsWith("ptolemy.actor.lib.logic")) { 817 types.add("urn:lsid:localhost:onto:2:1#BooleanControl"); 818 } 819 820 if(userLevelDocLowerCase.contains("filter") && 821 (userLevelDocLowerCase.contains("adaptive") || 822 userLevelDocLowerCase.contains("recursive") || 823 userLevelDocLowerCase.contains("lattice") || 824 userLevelDocLowerCase.contains("impulse response"))) { 825 types.add("urn:lsid:localhost:onto:2:1#Filtering"); 826 } 827 828 if(namedObjName.endsWith("Select") || 829 namedObjName.endsWith("Switch")) { 830 types.add("urn:lsid:localhost:onto:2:1#WorkflowControl"); 831 832 if(namedObjName.contains("Boolean")) { 833 types.add("urn:lsid:localhost:onto:2:1#BooleanControl"); 834 } 835 } 836 837 if(namedObjName.contains("Time") || 838 (namedObj instanceof TimedSource)) { 839 types.add("urn:lsid:localhost:onto:2:1#Time"); 840 } 841 842 if(namedObj instanceof TypedCompositeActor) { 843 types.add("urn:lsid:localhost:onto:2:1#Workflow"); 844 } 845 846 if((namedObj instanceof WebContent) || 847 namedObjClassName.startsWith("ptolemy.vergil.basic.export.web")) { 848 types.add("urn:lsid:localhost:onto:2:1#WorkflowWebExport"); 849 } 850 851 if(namedObjName.contains("XML") || 852 namedObjName.contains("XSLT")) { 853 types.add("urn:lsid:localhost:onto:2:1#XMLProcessor"); 854 } 855 856 return types; 857 } 858 859 /** Returns true if the specified class appears more than once 860 * in the cache content table. 861 */ 862 private static boolean _classHasMultipleDocs(String className) throws Exception { 863 864 if(_numClassDocFilesMap.isEmpty()) { 865 Connection conn = null; 866 Statement st = null; 867 ResultSet result = null; 868 try { 869 conn = DatabaseFactory.getDBConnection(); 870 st = conn.createStatement(); 871 872 result = st.executeQuery("select classname, count(classname) from cachecontenttable group by classname"); 873 874 while(result.next()) { 875 _numClassDocFilesMap.put(result.getString(1), result.getInt(2)); 876 } 877 878 } finally { 879 try { 880 if(result != null) { 881 result.close(); 882 } 883 if(st != null) { 884 st.close(); 885 } 886 if(conn != null) { 887 conn.close(); 888 } 889 } catch(SQLException e) { 890 MessageHandler.error("ERROR closing cache database.", e); 891 } 892 } 893 } 894 895 Integer count = _numClassDocFilesMap.get(className); 896 if(count == null) { 897 System.out.println("Class " + className + " not found in cache."); 898 return true; 899 } 900 return count > 1; 901 } 902 903 /** 904 * Change property (parameter) and port names in a KeplerDocumentationAttribute from 905 * the class field name to the name return by getName(). 906 */ 907 private static void _fixNames(NamedObj namedObj, 908 KeplerDocumentationAttribute doc, boolean printWhenChangeName) { 909 Class<?> clazz = namedObj.getClass(); 910 911 // get the property names 912 Map<String, String> docNameTable = new HashMap<String, String>( 913 doc.getPropertyHash()); 914 docNameTable.putAll(doc.getPortHash()); 915 for (String docName : docNameTable.keySet()) { 916 917 try { 918 // get the field for this property name 919 Field field = clazz.getField(docName); 920 if (field == null) { 921 System.out.println("ERROR: unable to find field for " 922 + docName); 923 } else { 924 // get the NamedObj for this field 925 NamedObj fieldNamedObj = (NamedObj) field.get(namedObj); 926 if (fieldNamedObj != null) { 927 928 // if the property is nested within another property, 929 // do not create docs for it 930 if(fieldNamedObj.getContainer() != namedObj) { 931 doc.removeProperty(docName); 932 continue; 933 } 934 935 // get the real name 936 String fullName = fieldNamedObj.getName(); 937 938 if(!fullName.equals(docName)) { 939 940 // remove the old name from the doc attribute and 941 // add the new one 942 String value; 943 if(fieldNamedObj instanceof Port) { 944 value = doc.removePort(docName); 945 doc.addPort(fullName, value); 946 } else { 947 value = doc.removeProperty(docName); 948 doc.addProperty(fullName, value); 949 } 950 951 if(printWhenChangeName) { 952 System.out.println("changed name " + docName 953 + " --> " + fullName); 954 } 955 } 956 } 957 } 958 } catch (Exception e) { 959 System.out.println(e.getClass() + " ERROR: " + e.getMessage()); 960 } 961 } 962 } 963 964 /** Get the next actor LSID from the README file. This method also 965 * updates the README file. 966 */ 967 private static String _getNextActorLSIDFromReadme() { 968 969 try { 970 971 ModuleTree modules = ModuleTree.instance(); 972 Module module = modules.getModuleByStemName("actors"); 973 String readmePath = module.getKarResourcesDir() + File.separator + "README"; 974 975 BufferedReader reader = new BufferedReader(new FileReader(readmePath)); 976 StringBuilder buf = new StringBuilder(); 977 978 Matcher matcher = null; 979 String line = reader.readLine(); 980 while(line != null) { 981 982 // see if line matches 983 matcher = _LAST_ACTOR_ID_PATTERN.matcher(line); 984 if(matcher.matches()) { 985 break; 986 } 987 988 buf.append(line); 989 line = reader.readLine(); 990 if(line != null) { 991 buf.append(System.getProperty("line.separator")); 992 } 993 } 994 995 if(matcher == null) { 996 System.out.println("WARNING: could not read LSID README file."); 997 reader.close(); 998 return null; 999 } 1000 1001 // parse last id from line and increment 1002 String idStr = matcher.group(1); 1003 int id = Integer.parseInt(idStr); 1004 id++; 1005 System.out.println(" incrementing README id to " + id); 1006 1007 // write id line to file 1008 buf.append(_LAST_ACTOR_ID_PREFIX + id + System.getProperty("line.separator")); 1009 1010 // write remaining file 1011 line = reader.readLine(); 1012 while(line != null) { 1013 buf.append(line); 1014 line = reader.readLine(); 1015 if(line != null) { 1016 buf.append(System.getProperty("line.separator")); 1017 } 1018 } 1019 reader.close(); 1020 1021 BufferedWriter writer = new BufferedWriter(new FileWriter(readmePath)); 1022 writer.write(buf.toString()); 1023 writer.close(); 1024 1025 1026 return "urn:lsid:kepler-project.org:actor:" + id + ":1"; 1027 1028 } catch (IOException e) { 1029 System.out.println("Error updating LSID README file: " + e.getMessage()); 1030 return null; 1031 } 1032 } 1033 1034 /** Get a file name from a class name or file name. */ 1035 private static String _getFileName(String name) throws ClassNotFoundException { 1036 // see if it's a file name 1037 if(name.endsWith(".java")) { 1038 return name; 1039 } else { 1040 // looks like a class name. see if we can figure out the 1041 // file name. 1042 String fileName = SuperClassPathFinderDoclet.getFileNameForClassName(name); 1043 if(fileName == null) { 1044 //System.out.println("ERROR: skipping " + name + 1045 //" since could not determine source file name."); 1046 return null; 1047 } 1048 return fileName; 1049 } 1050 } 1051 1052 /** Get a class name from a file name or class name. */ 1053 private static String _getClassName(String name) { 1054 1055 // if it looks like a file name, assume it's a class name 1056 if (!name.endsWith(".java")) { 1057 return name; 1058 } 1059 1060 String className = _filenameToClassMap.get(name); 1061 if (className == null) { 1062 1063 Main.execute(new String[] { "-quiet", "-doclet", 1064 "org.kepler.kar.SuperClassPathFinderDoclet", name }); 1065 className = SuperClassPathFinderDoclet.getClassName(); 1066 1067 _filenameToClassMap.put(name, className); 1068 //System.out.println("file name " + name + " class name " + className); 1069 1070 } 1071 return className; 1072 1073 } 1074 1075 private static void _initializeCache() { 1076 1077 // load the configuration since it is required to load the 1078 // KAR entries handlers, which in turn is required to build 1079 // the actor library 1080 1081 // NOTE: we must use a configuration with the GUI, otherwise 1082 // MoML filters are used to remove GUI actors. 1083 String spec = "ptolemy/configs/kepler/ConfigGUIAndCache.xml"; 1084 URL url = null; 1085 try { 1086 url = ConfigurationApplication.specToURL(spec); 1087 } catch (IOException e) { 1088 MessageHandler.error("ERROR configuration URL.", e); 1089 System.exit(1); 1090 } 1091 1092 try { 1093 ConfigurationApplication.readConfiguration(url); 1094 } catch (Exception e) { 1095 MessageHandler.error("ERROR reading configuration.", e); 1096 System.exit(1); 1097 } 1098 1099 // build the actor library in case it does not exist 1100 LibraryManager.getInstance().buildLibrary(); 1101 } 1102 1103 private static KeplerDocumentationAttribute _generateDocsFromDoclet(String fileName) { 1104 1105 // find parent files for this class 1106 Main.execute(new String[] { 1107 "-quiet", 1108 "-doclet", 1109 "org.kepler.kar.SuperClassPathFinderDoclet", fileName }); 1110 1111 final Set<String> classFiles = SuperClassPathFinderDoclet.getClassFiles(); 1112 final String className = SuperClassPathFinderDoclet.getClassName(); 1113 _filenameToClassMap.put(fileName, className); 1114 1115 if(classFiles.isEmpty()) { 1116 System.out.println("ERROR: could not find files of super classes of " + fileName); 1117 return null; 1118 } 1119 1120 // construct documentation for the class using its source file, and 1121 // those of its super classes 1122 final String[] args = new String[3 + classFiles.size()]; 1123 args[0] = "-quiet"; 1124 args[1] = "-doclet"; 1125 args[2] = "org.kepler.kar.KarDoclet"; 1126 int j = 3; 1127 for(String filename : classFiles) { 1128 args[j] = filename; 1129 j++; 1130 } 1131 1132 Main.execute(args); 1133 1134 return KarDoclet.getDoc(className); 1135 } 1136 1137 /** Remove parameter and port entries. TODO: remove ports */ 1138 /* 1139 private static void _removeDefaultValues(NamedObj namedObj) { 1140 1141 List<?> attributes = new LinkedList<Object>(namedObj.attributeList()); 1142 for(Object object : attributes) { 1143 String name = ((Attribute)object).getName(); 1144 if(!name.equals("KeplerDocumentation") && 1145 !name.startsWith("semanticType")) { 1146 1147 System.out.println("Removing attribute " + name); 1148 1149 try { 1150 ((Attribute)object).setContainer(null); 1151 } catch (Exception e) { 1152 System.out.println("ERROR removing attribute " + name + ": " + e.getMessage()); 1153 } 1154 } 1155 } 1156 1157 // XXX remove ports 1158 1159 } 1160 */ 1161 1162 private static void _shutdownCache(boolean exit) { 1163 // call the module deinitializers 1164 Kepler.shutdown(); 1165 HSQL.shutdownServers(); 1166 1167 if(exit) { 1168 // we have to call System.exit() because ??? 1169 System.exit(0); 1170 } 1171 } 1172 1173 /** The prefix string for the last known LSID. */ 1174 private final static String _LAST_ACTOR_ID_PREFIX = "The last known id for an actor is actor:"; 1175 1176 /** A pattern to match the last know LSID string. */ 1177 private final static Pattern _LAST_ACTOR_ID_PATTERN = Pattern.compile(_LAST_ACTOR_ID_PREFIX + "\\s*(\\d+)"); 1178 1179 private static Map<String,Integer>_numClassDocFilesMap = new HashMap<String,Integer>(); 1180 1181 private static Map<String,String> _filenameToClassMap = new HashMap<String,String>(); 1182 1183 private static Workspace _workspace = new Workspace(); 1184 private static MoMLParser _parser = new MoMLParser(_workspace); 1185 1186 static { 1187 KarDoclet.setWorkspace(_workspace); 1188 } 1189 1190 /** Regex to match actor names that perform conversions. */ 1191 private final static Pattern _CONVERT_FROM_TO_PATTERN = Pattern.compile(".+To[A-Z].+"); 1192}