You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by da...@apache.org on 2005/06/08 00:32:52 UTC

svn commit: r189461 [2/3] - in /cocoon/whiteboard/osgi: ./ ant/ ant/html_template/ ant/src/ ant/src/org/ ant/src/org/knopflerfish/ ant/src/org/knopflerfish/ant/ ant/src/org/knopflerfish/ant/taskdefs/ ant/src/org/knopflerfish/ant/taskdefs/bundle/ bundles/ bundles/cocoon/ bundles/cocoon/src/ bundles/cocoon_testcase/ bundles/cocoon_testcase/src/ bundles/cocoon_testcase/src/org/ bundles/cocoon_testcase/src/org/apache/ bundles/cocoon_testcase/src/org/apache/cocoon/ bundles/cocoon_testcase/src/org/apache/cocoon/service/ bundles/cocoon_testcase/src/org/apache/cocoon/service/cocoon_testcase/ bundles/cocoon_testcase/src/org/apache/cocoon/service/cocoon_testcase/impl/ bundles/test/ bundles/test/src/ bundles/test/src/org/ bundles/test/src/org/apache/ bundles/test/src/org/apache/cocoon/ bundles/test/src/org/apache/cocoon/service/ bundles/test/src/org/apache/cocoon/service/test/ bundles/test/src/org/apache/cocoon/service/test/impl/ jars-external/ jars-external/junit/ jars/ legal/

Added: cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleHTMLExtractorTask.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleHTMLExtractorTask.java?rev=189461&view=auto
==============================================================================
--- cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleHTMLExtractorTask.java (added)
+++ cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleHTMLExtractorTask.java Tue Jun  7 15:32:48 2005
@@ -0,0 +1,1061 @@
+/*
+ * Copyright (c) 2003, KNOPFLERFISH project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer. 
+ * 
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution. 
+ * 
+ * - Neither the name of the KNOPFLERFISH project nor the names of its 
+ *   contributors may be used to endorse or promote products derived 
+ *   from this software without specific prior written permission. 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.knopflerfish.ant.taskdefs.bundle;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.*;
+import org.apache.bcel.generic.Type;
+import org.apache.bcel.generic.BasicType;
+
+import java.io.*;
+import java.util.*;
+import java.util.jar.*;
+import java.util.zip.*;
+
+/**
+ * <p>
+ * Task that analyzes a set of bundle jar files and builds HTML documentation
+ * from these bundles. Also creates cross-references to bundle dependecies.
+ * </p>
+ *
+ * <p>
+ * All generated HTML will be stored in the same directory stucture as
+ * the scanned jars, e.g a jar file
+ * <pre>
+ *  jars/log/log-api.jar
+ * </pre>
+ * will have a corresponding
+ * <pre>
+ *  jars/log/log-api.html
+ * </pre>
+ *
+ * <p>
+ * Bundle jar files files are analyzed using the static manifest attributes.
+ * </p>
+ *
+ * <h3>Parameters</h3>
+ *
+ * <table border=>
+ *  <tr>
+ *   <td valign=top><b>Attribute</b></td>
+ *   <td valign=top><b>Description</b></td>
+ *   <td valign=top><b>Required</b></td>
+ *  </tr>
+ *  <tr>
+ *   <td valign=top>javadocRelPath</td>
+ *   <td valign=top>Relative path (from baseDir) to javadocs.
+ *   </td>
+ *   <td valign=top>No.<br> Default value is "."</td>
+ *  </tr>
+ *  <tr>
+ *   <td valign=top>baseDir</td>
+ *   <td valign=top>
+ *    Base directory for scanning for jar files.
+ *   </td>
+ *   <td valign=top>No.<br> Default value is "."</td>
+ *  </tr>
+ *   <td valign=top>templateHTMLDir</td>
+ *   <td valign=top>
+ *   Directory containing HTML template files. This directory must
+ *   contain the files:
+ *   <pre>
+ *    bundle_index.html
+ *    bundle_list.html
+ *    bundle_main.html
+ *    style.css
+ *   </pre>
+ *   </td>
+ *  </td>
+ *   <td valign=top>No.<br> Default value is "."</td>
+ *  </tr>
+ *
+ *  <tr>
+ *   <td valign=top>systemPackageSet</td>
+ *   <td valign=top>
+ *    Comma-spearated set of packages which are system packages and
+ *    thus globally available.
+ *    These are not cross-referenced.
+ *   </td>
+ *   <td valign=top>No.<br> 
+ * Default value is "javax.swing,javax.accessibility,javax.servlet,javax.xml,org.xml,org.w3c,java,com.sun"
+ </td>
+ *
+ *  <tr>
+ *   <td valign=top>skipAttribSet</td>
+ *   <td valign=top>
+ *    Comma-spearated set of manifest attributes which shouldn't be printed.
+ *   </td>
+ *   <td valign=top>No.<br> 
+ * Default value is "Manifest-Version,Ant-Version,Bundle-Config,Created-By,Built-From"
+ </td>
+ *
+ * </table>
+ *
+ * <h3>Parameters specified as nested elements</h3>
+ * <h4>fileset</h4>
+ *
+ * (required)<br>
+ * <p>
+ * All jar files must be specified as a fileset. No jar files  
+ * are ignored. 
+ * </p>
+ *
+ * <h3>Examples</h3>
+ *
+ * <pre>
+ * &lt;bundlehtml templateHTMLDir    = "${ant.dir}/html_template"
+ *                baseDir            = "${release.dir}/jars"
+ *		  javadocRelPath     = "../javadoc"
+ *   &gt;
+ *
+ *     &lt;fileset dir="${release.dir}/jars"&gt;	   
+ *       &lt;include name = "&ast;&ast;/&ast;.jar"/&gt;
+ *     &lt;/fileset&gt;
+ * </pre> 
+ *
+ */
+public class BundleHTMLExtractorTask extends Task {
+  
+  private Vector    filesets = new Vector();
+  private FileUtils fileUtils;
+  
+  private File   templateHTMLDir     = new File(".");
+  private String listSeparator       = "<br>\n";
+  private File   baseDir             = new File(".");
+  private String javadocRelPath      = null;
+
+ 
+  private String indexListRow = 
+    "<a target=\"bundle_main\" href=\"${bundledoc}\">${FILE.short}</a><br>";
+
+  private String indexMainRow = 
+    "<tr>" +
+    "<td><a target=\"bundle_main\" href=\"${bundledoc}\">${FILE.short}</a></td><td>" + 
+    "<td>${Bundle-Description}</td>" + 
+    "</tr>\n";
+
+  private String bundleRow    = 
+    "<tr><td><a href=\"${bundledoc}\">${FILE.short}</a></td><td>${what}</td></tr>\n";
+ 
+  private String missingRow   = 
+    "<tr><td>${name}</td><td>${version}</td></tr>\n";
+  
+  private String rowHTML      = 
+    "<a href=\"${bundle.uri}\">${FILE.short}</a><br>\n";
+
+  private String pkgHTML      = 
+    "${namelink} ${version}<br>";
+  
+  private boolean bCheckJavaDoc  = true;
+
+  Map     jarMap     = new TreeMap();
+  Map     globalVars = new TreeMap();
+
+
+  Map     missingDocs = new TreeMap();
+
+  public BundleHTMLExtractorTask() {
+
+    fileUtils = FileUtils.newFileUtils();
+
+    setListProps("Export-Package," + 
+		 "Import-Package," + 
+		 "Import-Service," + 
+		 "Export-Service");
+
+    setSystemPackageSet("javax.swing," + 
+			"javax.accessibility," +
+			"javax.servlet," +
+			"javax.xml," + 
+			"org.xml," +
+			"org.w3c," + 
+			"java," + 
+			"com.sun");
+    
+    setSkipAttribSet("Manifest-Version," + 
+		     "Ant-Version," + 
+		     "Bundle-Config," + 
+		     "Created-By"
+		     //		     "Built-From",
+		     );
+    
+    setAlwaysProps("Bundle-Activator," + 
+		   "Bundle-Vendor," +
+		   "Bundle-Name," +
+ 		   "Bundle-Description," +
+		   "Export-Package," + 
+		   "Import-Package," + 
+		   "Import-Service," + 
+		   "Export-Service," + 
+		   "Main-class," + 
+		   "Build-Date," + 
+		   "Bundle-DocURL," + 
+		   "Bundle-Classpath," + 
+		   "Bundle-ContactAddress," + 
+		   "Bundle-Activator");
+  }
+
+  public void setCheckJavaDoc(String s) {
+    this.bCheckJavaDoc = "true".equals(s);
+  }
+
+  public void setTemplateHTMLDir(String s) {
+    this.templateHTMLDir = new File(s);
+
+    if(!templateHTMLDir.exists()) {
+      throw new BuildException("templateHTMLDir: " + s + " does not exist");
+    }
+    if(!templateHTMLDir.isDirectory()) {
+      throw new BuildException("templateHTMLDir: " + s + " is not a directory");
+    }
+  }
+
+
+  File getBundleInfoTemplate() {
+    return new File(templateHTMLDir, "bundle_info.html");
+  }
+
+  File getBundleCSSTemplate() {
+    return new File(templateHTMLDir, "style.css");
+  }
+
+  File getBundleListTemplate() {
+    return new File(templateHTMLDir, "bundle_list.html");
+  }
+
+  File getBundleMainTemplate() {
+    return new File(templateHTMLDir, "bundle_main.html");
+  }
+
+  File getBundleIndexTemplate() {
+    return new File(templateHTMLDir, "bundle_index.html");
+  }
+
+  public void setBaseDir(String s) {
+    this.baseDir = new File((new File(s)).getAbsolutePath());
+  }
+  
+  public void setJavadocRelPath(String s) {
+    this.javadocRelPath = s;
+  }
+
+  public void addFileset(FileSet set) {
+    filesets.addElement(set);
+  }
+  
+  Set listPropSet      = new HashSet();
+  Set skipAttribSet    = new HashSet();
+  Set alwaysPropSet    = new HashSet();
+  Set systemPackageSet = new HashSet();
+
+  public void setListProps(String s) {
+    listPropSet = Util.makeSetFromStringList(s);
+  }
+
+  public void setAlwaysProps(String s) {
+    alwaysPropSet = Util.makeSetFromStringList(s);
+  }
+
+  public void setSkipAttribSet(String s) {
+    skipAttribSet = Util.makeSetFromStringList(s);
+  }
+
+  public void setSystemPackageSet(String s) {
+    systemPackageSet = Util.makeSetFromStringList(s);
+  }
+
+  // Implements Task
+  public void execute() throws BuildException {
+    if (filesets.size() == 0) {
+      throw new BuildException("No fileset specified");
+    }
+    
+    try {
+      for (int i = 0; i < filesets.size(); i++) {
+	FileSet          fs      = (FileSet) filesets.elementAt(i);
+	DirectoryScanner ds      = fs.getDirectoryScanner(project);
+	File             projDir = fs.getDir(project);
+	
+	String[] srcFiles = ds.getIncludedFiles();
+	String[] srcDirs  = ds.getIncludedDirectories();
+	
+	for (int j = 0; j < srcFiles.length ; j++) {
+	  File file = new File(projDir, srcFiles[j]); 
+	  if(file.getName().endsWith(".jar")) {
+	    jarMap.put(file, new BundleInfo(file));
+	  }
+	}
+      }
+      
+      System.out.println("analyzing " + jarMap.size() + " bundles");
+      
+      for(Iterator it = jarMap.keySet().iterator(); it.hasNext();) {
+	File       file = (File)it.next();
+	BundleInfo info = (BundleInfo)jarMap.get(file);
+	
+	info.load();
+      }
+      
+      System.out.println("writing bundle info html pages");      
+      for(Iterator it = jarMap.keySet().iterator(); it.hasNext();) {
+	File       file = (File)it.next();
+	BundleInfo info = (BundleInfo)jarMap.get(file);
+	
+	info.writeInfo();
+      }
+
+      makeListPage(getBundleMainTemplate(),
+		   new File(baseDir, "main.html"),
+		   indexMainRow);
+
+      makeListPage(getBundleListTemplate(),
+		   new File(baseDir, "list.html"),
+		   indexListRow);
+            
+      copyFile(getBundleIndexTemplate(),
+	       new File(baseDir, "index.html"));
+      
+      copyFile(getBundleCSSTemplate(),
+	       new File(baseDir, "style.css"));
+	       
+
+      for(Iterator it = missingDocs.keySet().iterator(); it.hasNext();) {
+	String name = (String)it.next();
+
+	System.out.println("Missing javadoc for " + name);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw new BuildException("Failed to extract bundle info: " + e, e);
+    }
+  }
+
+  void makeListPage(File templateFile, 
+		    File outFile,
+		    String rowTemplate) 
+    throws IOException {
+
+    int unresolvedCount = 0;
+
+    String html = Util.loadFile(templateFile.getAbsolutePath());
+
+    StringBuffer sb = new StringBuffer();
+    for(Iterator it = jarMap.keySet().iterator(); it.hasNext();) {
+      File       file = (File)it.next();
+      BundleInfo info = (BundleInfo)jarMap.get(file);
+      
+      unresolvedCount += info.unresolvedMap.size();
+
+      String row = rowTemplate;
+      
+      row = info.stdReplace(row, false);
+      
+      row = replace(row,
+		    "${bundledoc}",
+		    replace(info.path, ".jar", ".html"));
+      
+      sb.append(row);
+    }
+    
+
+    
+    html = replace(html, "${bundle.list}", sb.toString());
+
+    sb = new StringBuffer();
+
+    if(unresolvedCount > 0) {
+
+      sb.append("<table>\n");
+      sb.append("<tr>\n" + 
+		" <td colspan=2 class=\"mfheader\">Unresolved packages</td>\n" +
+		"</tr>\n");
+      
+      for(Iterator it = jarMap.keySet().iterator(); it.hasNext();) {
+	File       file = (File)it.next();
+	BundleInfo info = (BundleInfo)jarMap.get(file);
+
+	if(info.unresolvedMap.size() > 0) {
+	  sb.append("<tr>\n" + 
+		    " <td>" + info.file.getName() + "</td>\n");
+	  
+	  sb.append("<td>");
+	  for(Iterator it2 = info.unresolvedMap.keySet().iterator(); 
+	      it2.hasNext();)
+	    {
+	      String   pkgName = (String)it2.next();
+	      ArrayInt version = (ArrayInt)info.unresolvedMap.get(pkgName);
+	      
+	      sb.append(pkgName + " " + version + "<br>\n");
+	    }
+	  sb.append("</td>");
+	  sb.append("<tr>");
+	}
+      }
+      sb.append("</table>\n");
+    }
+
+    html = replace(html, "${unresolved.list}", sb.toString());
+
+
+
+
+
+    Util.writeStringToFile(outFile, html);    
+    System.out.println("wrote " + outFile);
+	
+  }
+
+  void copyFile(File templateFile, File outFile)
+    throws IOException {
+
+      String src = Util.loadFile(templateFile.getAbsolutePath());	
+
+      Util.writeStringToFile(outFile, src);
+      System.out.println("copied " + outFile);
+  }
+  
+  interface MapSelector {
+    public Map getMap(BundleInfo info);
+  }
+
+
+  class BundleInfo {
+    File       file;
+    Attributes attribs;
+    Map        vars      = new TreeMap();
+
+    // String (package name) -> ArrayInt (version)
+    Map        pkgImportMap = new TreeMap();
+    Map        pkgExportMap = new TreeMap();
+    Map        serviceExportMap = new TreeMap();
+    Map        serviceImportMap = new TreeMap();
+
+    String     relPath = "";
+    String     path    = "";
+
+    Map unresolvedMap = new TreeMap();
+
+    public BundleInfo(File file) throws IOException  {
+      this.file = file;
+      relPath = "";
+      File dir = file.getParentFile();
+
+      while(dir != null && !dir.equals(baseDir)) {
+	//	System.out.println("** ! " + dir + " || " + baseDir);
+	relPath += "../";
+	dir = dir.getParentFile();
+
+      }
+
+      if(dir == null) {
+	throw new BuildException(baseDir.getAbsolutePath() + " is not parent of " + file.getAbsolutePath());
+      }
+
+      if(relPath.equals("")) {
+	//	relPath = ".";
+      }
+
+
+      path = replace(file.getCanonicalPath().substring(1 + baseDir.getCanonicalPath().length()), "\\", "/");
+      
+      //      System.out.println(file + ", " + relPath + ", " + baseDir.getAbsolutePath() + ", path=" + path);
+    }
+    
+    public void load() throws Exception {
+      JarFile    jarFile      = new JarFile(file);
+      Manifest   mf           = jarFile.getManifest();
+      attribs                 = mf.getMainAttributes();
+
+      vars.put("html.file", replace(file.toString(), ".jar", ".html"));
+
+      String absBase = baseDir.getCanonicalPath();
+      
+      String  htmlFilename = (String)vars.get("html.file");
+	
+      File htmlFile = new File(htmlFilename);
+      
+      String absFile = htmlFile.getCanonicalPath();
+      
+      if(!absFile.startsWith(absBase)) {
+	System.out.println("*** base dir is not parent of html file");
+	System.out.println("base dir:  " + absBase);
+	System.out.println("html file: " + absFile);
+      } else {
+	String relPath = absFile.substring(absBase.length() + 1);
+	
+	//	  System.out.println("absFile=" + absFile);
+	//	  System.out.println("relPath=" + relPath);
+	
+	vars.put("html.uri", replace(relPath, "\\", "/"));
+      }
+
+      pkgExportMap     = parseNames(attribs.getValue("Export-Package"));
+      pkgImportMap     = parseNames(attribs.getValue("Import-Package"));
+      serviceExportMap = parseNames(attribs.getValue("Export-Service"));
+      serviceImportMap = parseNames(attribs.getValue("Import-Service"));
+
+      if(true) {
+	extractSource(jarFile, new File(replace(file.getAbsolutePath(), ".jar", "") + "/src"));
+      }
+    }
+
+    
+    // String -> String
+    Map sourceMap = new TreeMap();
+    boolean bSourceInside = false;
+
+    void extractSource(JarFile jarFile, File destDir) throws IOException {
+
+      String prefix = "OSGI-OPT/src";
+
+      int count = 0;
+      for(Enumeration e = jarFile.entries(); e.hasMoreElements(); ) {
+	ZipEntry entry = (ZipEntry)e.nextElement();
+	
+	if(entry.getName().startsWith("OSGI-OPT/src")) {
+	  count++;
+	}
+      }
+
+
+      if(count > 0) {
+	bSourceInside = true;
+
+	//	System.out.println("found " + count + " source files in " + jarFile.getName());
+
+	//	System.out.println("creating "+ destDir.getAbsolutePath());
+	destDir.mkdirs();
+
+	for(Enumeration e = jarFile.entries(); e.hasMoreElements(); ) {
+	  ZipEntry entry = (ZipEntry)e.nextElement();
+	  
+	  if(entry.getName().startsWith(prefix)) {
+	    if(entry.isDirectory()) {
+	      makeDir(jarFile, entry, destDir, prefix);
+	    } else {
+	      copyEntry(jarFile, entry, destDir, prefix);
+
+	      String s = replace(entry.getName(), prefix + "/", "");
+	      sourceMap.put(s, s);
+	    }
+	    count++;
+	  }
+	}
+
+      } else {	
+	// Check if we can copy source from original pos
+	String sourceDir = (String)attribs.getValue("Built-From");
+	if(sourceDir != null && !"".equals(sourceDir)) {
+	  File src = new File(sourceDir, "src");
+	  copyTree(src, destDir, sourceDir + "\\src\\", ".java");
+	}
+      }
+    }
+
+    void copyTree(File src, File dest, 
+		  String prefix,
+		  String suffix) throws IOException {
+      if(src.isDirectory()) {
+	if(!dest.exists()) {
+	  dest.mkdirs();
+	}
+	
+	String[] files = src.list();
+	for(int i = 0; i < files.length; i++) {
+	  copyTree(new File(src, files[i]),
+		   new File(dest, files[i]),
+		   prefix,
+		   suffix);
+	}
+      } else if(src.isFile()) {
+	if(src.getName().endsWith(suffix)) {
+	  String path = src.getAbsolutePath();
+
+	  String s = replace(replace(path, prefix, ""), "\\", "/");
+	  
+	  copyStream(new FileInputStream(src),  new FileOutputStream(dest));
+	  sourceMap.put(s, s);
+	}
+      }
+    }
+    
+    
+    void makeDir(ZipFile file, 
+		 ZipEntry entry, 
+		 File destDir,
+		 String prefix) throws IOException {
+      File d = new File(destDir, replace(entry.getName(), prefix, ""));
+      
+      d.mkdirs();
+      //      System.out.println("created dir  " + d.getAbsolutePath());
+    }
+    
+    
+    void copyEntry(ZipFile file, 
+		   ZipEntry entry, 
+		   File destDir,
+		   String prefix) throws IOException {
+      File d = new File(destDir, replace(entry.getName(), prefix, ""));
+      
+      File dir = d.getParentFile();
+      
+      if(!dir.exists()) {
+	dir.mkdirs();
+      }
+      
+      //      System.out.println("extracting to " + d.getAbsolutePath());
+      
+      copyStream(new BufferedInputStream(file.getInputStream(entry)),
+		 new BufferedOutputStream(new FileOutputStream(d)));
+    }
+    
+
+    void copyStream(InputStream is, OutputStream os) throws IOException {
+      byte[] buf = new byte[1024];
+      
+      
+      BufferedInputStream in   = null;
+      BufferedOutputStream out = null;
+
+      try {
+	in  = new BufferedInputStream(is);
+	out = new BufferedOutputStream(os);
+	int n;
+	int total = 0;
+	while ((n = in.read(buf)) > 0) {
+	  out.write(buf, 0, n);
+	  total += n;
+	}
+      } finally {
+	try { in.close(); } catch (Exception ignored) { } 
+	try { out.close(); } catch (Exception ignored) { } 
+      }
+    }
+
+    
+
+    Map parseNames(String s) {
+      
+      Map map = new TreeMap();
+
+      //      System.out.println(file + ": " + s);
+      if(s != null) {
+	s = s.trim();
+	String[] lines = Util.splitwords(s, ",", '\"');
+	for(int i = 0; i < lines.length; i++) {
+	  String[] words = Util.splitwords(lines[i].trim(), ";", '\"');
+	  if(words.length < 1) {
+	    throw new RuntimeException("bad package spec '" + s + "'"); 
+	  }
+	  String spec = ArrayInt.UNDEF;
+	  String name = words[0].trim();
+
+	  for(int j = 1; j < words.length; j++) {
+	    String[] info = Util.splitwords(words[j], "=", '\"');
+
+	    if(info.length == 2) {
+	      if("specification-version".equals(info[0].trim())) {
+		spec = info[1].trim();
+	      }
+	    }
+	  }
+	  
+	  //	  System.out.println(" " + i + ": " + name + ", version=" + spec);
+	  ArrayInt version = new ArrayInt(spec);
+
+	  map.put(name, version);
+	}
+      }
+
+      return map;
+    }
+
+    public void writeInfo() throws IOException {
+      
+      //    System.out.println("jar info from " + file);
+      
+      String template = stdReplace(Util.load(getBundleInfoTemplate().getAbsolutePath()));
+      String outName  = (String)vars.get("html.file");
+      
+      Util.writeStringToFile(new File(outName), template);
+    }
+    
+    public String stdReplace(String template) throws IOException {
+      return stdReplace(template, true);
+    }
+
+
+    public String stdReplace(String template, boolean bDepend) throws IOException {
+
+      for(Iterator it = globalVars.keySet().iterator(); it.hasNext(); ) {
+	Object key = it.next();
+	Object val = globalVars.get(key);
+	
+	template = replace(template, "${" + key + "}", "" + val);
+	
+	//      System.out.println(key + "->" + val);
+      }
+
+      for(Iterator it = vars.keySet().iterator(); it.hasNext(); ) {
+	Object key = it.next();
+	Object val = vars.get(key);
+	
+	template = replace(template, "${" + key + "}", "" + val);
+	
+	//      System.out.println(key + "->" + val);
+      }
+      
+      Set handledSet = new TreeSet();
+      
+      for(Iterator it = attribs.keySet().iterator(); it.hasNext(); ) {
+	Object key = it.next();
+
+	if(-1 != template.indexOf("${" + key.toString() + "}")) {
+	  handledSet.add(key.toString());
+	}
+      }
+
+      for(Iterator it = attribs.keySet().iterator(); it.hasNext(); ) {
+	Object key = it.next();
+	Object val = attribs.get(key);
+	
+      
+	String str = (String)attribs.get(key);
+	
+	if("Export-Package".equals(key.toString()) ||
+	   "Import-Package".equals(key.toString()) ||
+	   "Import-Service".equals(key.toString()) ||
+	   "Export-Service".equals(key.toString())) {
+	} else {
+	  if(listPropSet.contains(key.toString())) {
+	    str = replace(str, ",", listSeparator);
+	  }
+	  
+	  template = replace(template, "${" + key + "}", str);
+	}
+      }
+      
+      
+      template = replace(template, 
+			 "${Export-Package}", 
+			 getPackageString(pkgExportMap, 
+					  "/package-summary.html"));
+      
+      template = replace(template, 
+			 "${Import-Package}", 
+			 getPackageString(pkgImportMap, 
+					  "/package-summary.html"));
+      
+
+      template = replace(template, 
+			 "${Export-Service}", 
+			 getPackageString(serviceExportMap, 
+					  ".html"));
+      
+      template = replace(template, 
+			 "${Import-Service}", 
+			 getPackageString(serviceImportMap, 
+					  ".html"));
+      
+      
+      for(Iterator it = alwaysPropSet.iterator(); it.hasNext(); ) {
+	String key = (String)it.next();
+	String val = "";
+	
+	template = replace(template, "${" + key + "}", val);
+      }
+
+      StringBuffer sb = new StringBuffer();
+
+      for(Iterator it = attribs.keySet().iterator(); it.hasNext(); ) {
+	Object key = it.next();
+	
+	if(!handledSet.contains(key.toString())) {
+	  if(!skipAttribSet.contains(key.toString())) {
+	    sb.append("<tr>\n" + 
+		      " <td>" + key + "</td>\n" + 
+		      " <td>" + attribs.getValue(key.toString()) + "</td>\n" + 
+		      "</tr>\n");
+	  }
+	}
+      }
+      
+      template = replace(template,  "${MF.UNHANDLED}",  sb.toString());
+      
+      template = replace(template,  "${FILE}",  file.getName());
+      template = replace(template,  "${FILE.short}",  replace(file.getName(), ".jar", ""));
+      template = replace(template,  "${BYTES}", "" + file.length());
+      template = replace(template,  "${KBYTES}", "" + (file.length() / 1024));
+      
+      template = replace(template,  "${relpath}",     relPath);
+      template = replace(template,  "${javadocdir}",  
+			 javadocRelPath != null ? javadocRelPath : "");
+      
+
+      if(bDepend) {
+	unresolvedMap = new TreeMap();
+
+	template = replace(template, 
+			   "${depending.list}", 
+			   getDepend(
+				     new MapSelector() {
+					 public Map getMap(BundleInfo info) {
+					   return info.pkgExportMap;
+					 }
+				       },
+				     new MapSelector() {
+					 public Map getMap(BundleInfo info) {
+					   return info.pkgImportMap;
+					 }
+				       },
+				     null,
+				     false));
+	
+	template = replace(template, 
+			   "${depends.list}",
+			   getDepend(
+				     new MapSelector() {
+					 public Map getMap(BundleInfo info) {
+					   return info.pkgImportMap;
+					 }
+				       },
+				     new MapSelector() {
+					 public Map getMap(BundleInfo info) {
+					   return info.pkgExportMap;
+					 }
+				       },
+				     unresolvedMap,
+				     true));
+	
+      }
+
+      sb = new StringBuffer();
+      if(sourceMap.size() > 0) {
+
+	String srcBase = replace(file.getName(), ".jar", "") + "/src";
+	
+	sb.append("<table>");
+	for(Iterator it = sourceMap.keySet().iterator(); it.hasNext();) {
+	  String name = (String)it.next();
+	  
+	  
+	  sb.append(" <tr>\n");
+	  sb.append("  <td>\n");
+	  sb.append("  <a href=\"" + srcBase + "/" + name + "\">" + name + "<a>\n");
+	  sb.append(" </tr>\n");
+	  sb.append(" </tr>\n");
+	  
+	}
+	sb.append("</table>");
+	
+      } else{
+	sb.append("None found");
+      }
+
+      template = replace(template, "${sources.list}", sb.toString());
+      if(bSourceInside && sourceMap.size() > 0) {
+	template = replace(template, 
+			   "${FILEINFO}", 
+			   file.length() + " bytes, includes <a href=\"#source\">source</a>");
+      } else {
+	template = replace(template, 
+			   "${FILEINFO}", 
+			   file.length() + " bytes");
+      }
+      
+
+      return template;
+    }
+  
+    
+    String getDepend(MapSelector importMap, 
+		     MapSelector exportMap,
+		     Map         unresolvedMapDest,
+		     boolean bShowUnresolved) throws IOException {
+
+      Map map        = new TreeMap();
+
+      if(unresolvedMapDest == null) {
+	unresolvedMapDest = new TreeMap();
+      }
+
+      for(Iterator it = importMap.getMap(this).keySet().iterator(); it.hasNext(); ) {
+	String   name    = (String)it.next();
+	ArrayInt version = (ArrayInt)importMap.getMap(this).get(name);
+	
+	boolean bFound = false;
+	for(Iterator it2 = jarMap.keySet().iterator(); it2.hasNext();) {
+	  File jarFile = (File)it2.next();
+	  BundleInfo info = (BundleInfo)jarMap.get(jarFile);
+	  
+	  for(Iterator it3 = exportMap.getMap(info).keySet().iterator(); it3.hasNext(); ) {
+	    String   name2    = (String)it3.next();
+	    ArrayInt version2 = (ArrayInt)exportMap.getMap(info).get(name);
+	    
+	    if(name.equals(name2)) {
+	      if(version2.compareTo(version) >= 0) {
+		bFound = true;
+		map.put(jarFile, name);
+	      } else {
+		System.out.println(file + ": need " + name + " version " + version + ", found version " + version2);
+	      }
+	    }
+	  }
+	}
+
+	if(!bFound && !isSystemPackage(name)) {
+	  unresolvedMapDest.put(name, version);
+	}
+
+      }
+      StringBuffer sb = new StringBuffer();
+      
+      if(map.size() == 0 && unresolvedMapDest.size() == 0) {
+	sb.append("None found");
+      } else {
+	for(Iterator it = map.keySet().iterator(); it.hasNext();) {
+	  Object     key     = it.next();
+	  File       jarFile = (File)key;
+	  BundleInfo info    = (BundleInfo)jarMap.get(jarFile);
+	  String     what    = (String)map.get(jarFile);
+	  
+	  String row = info.stdReplace(bundleRow, false);
+	  
+	  row = replace(row, "${what}", what);
+	  row = replace(row, 
+			"${bundledoc}", 
+			replace(relPath + info.path, ".jar", ".html"));
+	  sb.append(row);
+	}
+
+
+	if(bShowUnresolved) {
+	  if(unresolvedMapDest.size() > 0) {
+	    String row = missingRow;
+	    
+	    row = replace(row, "${name}",    "<b>Unresolved</b>");
+	    row = replace(row, "${version}", "");
+	    
+	    sb.append(row);
+	  }
+	  for(Iterator it = unresolvedMapDest.keySet().iterator(); it.hasNext();) {
+	    String   name    = (String)it.next();
+	    ArrayInt version = (ArrayInt)unresolvedMapDest.get(name);
+	    
+	    String row = missingRow;
+	    
+	    row = replace(row, "${name}",    name);
+	    row = replace(row, "${version}", version.toString());
+	    
+	    sb.append(row);
+	    
+	  }
+	}
+
+      }
+      return sb.toString();
+    }
+	
+
+
+    String getPackageString(Map map, String linkSuffix) {
+      StringBuffer sb = new StringBuffer();
+      
+      for(Iterator it = map.keySet().iterator(); it.hasNext(); ) {
+	String   name    = (String)it.next();
+	ArrayInt version = (ArrayInt)map.get(name);
+	
+	String html = pkgHTML;
+
+	String docFile = replace(name, ".", "/") + linkSuffix;	
+	String docPath = relPath + javadocRelPath + "/" + docFile;
+	
+	File f = new File(file.getParentFile(), docPath);
+
+
+	if(javadocRelPath != null && !"".equals(javadocRelPath)) {
+	  if(bCheckJavaDoc && !f.exists() && !isSystemPackage(name)) {
+	    html = replace(html, "${namelink}", "${name}");
+
+	    missingDocs.put(name, this);
+	  } else {
+	    html = replace(html, 
+			 "${namelink}", "<a href=\"${javadoc}\">${name}</a>");
+	  }
+	} else {
+	  html = replace(html, "${namelink}", "${name}");
+	}
+	
+	String row     = replace(html, "${name}", name);
+
+	row = replace(row, "${version}", version.toString());
+	row = replace(row, "${javadoc}", docPath);
+      
+	sb.append(row);
+      }
+
+      return sb.toString();
+    }
+  }
+  
+ 
+  boolean isSystemPackage(String name) {
+    for(Iterator it = systemPackageSet.iterator(); it.hasNext();) {
+      String prefix = (String)it.next();
+      if(name.startsWith(prefix)) {
+	return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  String replace(String src, String a, String b) {
+    return Util.replace(src, a, b == null ? "" : b);
+  }
+
+
+}
+
+

Added: cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleInfoTask.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleInfoTask.java?rev=189461&view=auto
==============================================================================
--- cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleInfoTask.java (added)
+++ cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleInfoTask.java Tue Jun  7 15:32:48 2005
@@ -0,0 +1,788 @@
+/*
+ * Copyright (c) 2003, KNOPFLERFISH project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer. 
+ * 
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution. 
+ * 
+ * - Neither the name of the KNOPFLERFISH project nor the names of its 
+ *   contributors may be used to endorse or promote products derived 
+ *   from this software without specific prior written permission. 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.knopflerfish.ant.taskdefs.bundle;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.*;
+import org.apache.bcel.generic.Type;
+import org.apache.bcel.generic.BasicType;
+
+import java.io.*;
+import java.util.*;
+
+/**
+ * Task that analyzes a set of java sources or class files, and lists all
+ * imported and defined packages. Also tries to find any class implementing
+ * <tt>org.osgi.framework.BundleActivator</tt>.
+ *
+ * <ul>
+ * <li><b>Class files</b>
+ * <p>
+ * Java class files are analyzed using the BCEL lib available from
+ * <a href="http://jakarta.apache.org/bcel">http://jakarta.apache.org/bcel</a>.
+ * <br>
+ * <b>Note</b>: Scanning <i>only</i> applies
+ * to the listed files - a complete closure of all referenced classes is
+ * not done.
+ * </p>
+ *
+ * <li><b>Source code</b>
+ * <p>
+ * Java source code is analyzed using very simple line-based scanning of 
+ * files. <br>
+ * <b>Note</b>: Source code analysis does not attempt to find any
+ * <tt>BundleActivator</tt>
+ * </p>
+ * 
+ * <li><b>Jar files</b>
+ * <p>
+ * Jar file analysis is not yet implemented
+ * </p>
+ *
+ * </ul>
+ *
+ * <h3>Parameters</h3>
+ *
+ * <table border=>
+ *  <tr>
+ *   <td valign=top><b>Attribute</b></td>
+ *   <td valign=top><b>Description</b></td>
+ *   <td valign=top><b>Required</b></td>
+ *  </tr>
+ *  <tr>
+ *   <td valign=top>imports</td>
+ *   <td valign=top>Name of property that will receive a comma-separated list 
+ *       of all used packages. 
+ *       <p>
+ *       If set to empty string, no property will be set.
+ *       </p>
+ *       <p>
+ *       <b>Note</b>: Some default packages are always added. These 
+ *       defaults can be set using the <tt>defaultimports</tt> parameter.
+ *       </p>
+ *   </td>
+ *   <td valign=top>No.<br> Default value is ""</td>
+ *  </tr>
+ *  <tr>
+ *   <td valign=top>exports</td>
+ *   <td valign=top>Name of property that will receive a comma-separated 
+ *       list of all defined packages. 
+ *       <p>
+ *       If set to empty string, no property will be set.
+ *       </p>
+ *   </td>
+ *   <td valign=top>No.<br> Default value is ""</td>
+ *  </tr>
+ *   <td valign=top>activator</td>
+ *   <td valign=top>
+ *       Name of property that will receive name of class which implements 
+ *       <tt>org.osgi.framework.BundleActivator</tt>
+ *       <p>
+ *       If set to empty string, no property will be set.
+ *       </p>
+ *       <p>
+ *       If set to non-empty, and multiple activators are found, or
+ *       an activator not equal to any previous content in the named property
+ *       is found - a warning will be logged.
+ *       </p>
+ *  </td>
+ *   <td valign=top>No.<br> Default value is ""</td>
+ *  </tr>
+ *  <tr>
+ *   <td valign=top>stdimports</td>
+ *   <td valign=top>
+ *       Comma-separated list of all prefixes to standard packages that 
+ *       should be ignored in exports list.
+ *   </td>
+ *   <td valign=top>
+ *     No.<br>
+ *     Default value is "java."
+ *   </td>
+ *  </tr>
+ *
+ *  <tr>
+ *   <td valign=top>defaultimports</td>
+ *   <td valign=top>
+ *       Comma-separated list of all default imported packages. 
+ *       <p>
+ *       <b>Note</b>: Do not set <tt>defaultimports</tt> to the empty 
+ *       string, since that might 
+ *       cause an later illegal bundle manifest file if <i>no</i> imported 
+ *       packages are found.
+ *       </p>
+ *   </td>
+ *   <td valign=top>
+ *     No.<br> 
+ *     Default value is "org.osgi.framework"
+ *   </td>
+ *  </tr>
+ *
+ *  <tr>
+ *   <td valign=top>checkFoundationEE</td>
+ *   <td valign=top>
+ *       Flag for testing for the Foundation Execution Environment
+ *       <p>
+ *       If set to "true", the task will check if all used classes
+ *       is in the set of the OSGi Foundation Execution Environment.
+ *       </p>
+ *   </td>
+ *   <td valign=top>
+ *     No.<br> 
+ *     Default value is "false"
+ *   </td>
+ *  </tr>
+ *
+ * <h3>Parameters specified as nested elements</h3>
+ * <h4>fileset</h4>
+ *
+ * (required)<br>
+ * <p>
+ * All files must be specified as a fileset. Unsupported file types 
+ * are ignored. 
+ * </p>
+ *
+ * <h3>Examples</h3>
+ *
+ * <h4>Check all imports and activator in implementation classes</h4>
+ *
+ * <p>
+ * This example assumes all implemention classes are in the package
+ * <tt>test.impl.*</tt>
+ * </p>
+ *
+ * <pre>
+ *  &lt;bundleinfo  activator = "bundle.activator" 
+ *               imports   = "impl.import.package"&gt;
+ *   &lt;fileset dir="classes" includes="test/impl/&#042;"/&gt;
+ *  &lt;/bundleinfo&gt;
+ *  &lt;echo message="imports   = ${impl.import.package}"/&gt;
+ *  &lt;echo message="activator = ${bundle.activator}"/&gt;
+ * </pre> 
+ *
+ *
+ * <h4>Check all imports and exports in API classes</h4>
+ *
+ * <p>
+ * This example assumes all API classes are in the package
+ * <tt>test.*</tt>
+ * </p>
+ *
+ * <pre>
+ *  &lt;bundleinfo  exports  = "api.export.package" 
+ *               imports  = "api.import.package"&gt;
+ *   &lt;fileset dir="classes" includes="test/&#042;"/&gt;
+ *  &lt;/bundleinfo&gt;
+ *  &lt;echo message="imports  = ${api.import.package}"/&gt;
+ *  &lt;echo message="exports  = ${api.export.package}"/&gt;
+ * </pre> 
+ * 
+ */
+public class BundleInfoTask extends Task {
+  
+  private Vector    filesets = new Vector();
+  private FileUtils fileUtils;
+  
+  private String importsProperty   = "";
+  private String exportsProperty   = "";
+  private String activatorProperty = "";
+  private String mainProperty      = "";
+  
+  private Set     stdImports       = new TreeSet();
+  private boolean bDebug           = false;
+
+  private boolean bPrintClasses      = false;
+
+  private boolean bCheckFoundationEE = false;
+  private boolean bCheckMinimumEE    = false;
+  private boolean bCheckSMFEE        = false;
+
+  private Set importSet            = new TreeSet();
+  private Set exportSet            = new TreeSet();
+  private Set activatorSet         = new TreeSet();
+
+  private Set classSet             = new TreeSet();
+  private Set ownClasses           = new TreeSet();
+  
+  public BundleInfoTask() {
+    fileUtils = FileUtils.newFileUtils();
+    setDefaultImports("org.osgi.framework");
+    setStdImports("java.");
+  }
+  
+  /**
+   * Set property receiving list of imported packages.
+   */
+  public void setImports(String s) {
+    this.importsProperty  = s;
+  }
+
+  public void setPrintClasses(String s) {
+    this.bPrintClasses = "true".equals(s);
+  }
+
+  public void setCheckFoundationEE(String s) {
+    this.bCheckFoundationEE = "true".equals(s);
+  }
+
+  public void setCheckMinimumEE(String s) {
+    this.bCheckMinimumEE = "true".equals(s);
+  }
+
+  public void setCheckSMFEE(String s) {
+    this.bCheckSMFEE = "true".equals(s);
+  }
+
+  /**
+   * Set default import set.
+   *
+   * @param packageList Comma-separated list of package names.
+   */
+  public void setDefaultImports(String packageList) {
+    Vector v = StringUtils.split(packageList.trim(),',');  
+    importSet.clear();
+    importSet.addAll(v);
+  }
+
+
+  /**
+   * Set property receiving list of exported packages.
+   */
+  public void setExports(String propName) {
+    this.exportsProperty = propName;
+  }
+
+  /**
+   * Set property receiving any BundleActivator class.
+   */
+  public void setActivator(String propName) {
+    this.activatorProperty = propName;
+  }
+
+  /**
+   * Set property receiving any Main class.
+   * <p>
+   * Not yet implemented.
+   * </p>
+   */
+  public void setMain(String propName) {
+    this.mainProperty = propName;
+  }
+
+
+  /**
+   * Set set of packages always imported.
+   *
+   * @param packageList Comma-separated list of package names.
+   */
+  public void setStdImports(String packageList) {
+    stdImports.clear();
+    stdImports.addAll(StringUtils.split(packageList.trim(),','));
+  }
+  
+  public void addFileset(FileSet set) {
+    filesets.addElement(set);
+  }
+  
+  // Implements Task
+  // 
+  // Scan all files in fileset and delegate to analyze()
+  // then write back values to properties.
+  public void execute() throws BuildException {
+    if (filesets.size() == 0) {
+      throw new BuildException("No fileset specified");
+    }
+    
+    for (int i = 0; i < filesets.size(); i++) {
+      FileSet fs = (FileSet) filesets.elementAt(i);
+      DirectoryScanner ds = fs.getDirectoryScanner(project);
+      File fromDir = fs.getDir(project);
+      
+      String[] srcFiles = ds.getIncludedFiles();
+      String[] srcDirs = ds.getIncludedDirectories();
+      
+      for (int j = 0; j < srcFiles.length ; j++) {
+	analyze(new File(fromDir, srcFiles[j]));
+      }
+      
+      for (int j = 0; j < srcDirs.length ; j++) {
+	analyze(new File(fromDir, srcDirs[j]));
+      }
+    }
+
+    // Scan done - write back properties
+
+    Project proj = getProject();
+
+    importSet.removeAll(exportSet);
+
+    if(!"".equals(importsProperty)) {
+      proj.setNewProperty(importsProperty,   toString(importSet, ","));
+    }
+
+    if(!"".equals(exportsProperty)) {
+      proj.setNewProperty(exportsProperty,  toString(exportSet, ","));
+    }
+
+    // Try to be a bit clever when writing back bundle activator
+    if(!"".equals(activatorProperty)) {
+      switch(activatorSet.size()) {
+      case 0:
+	System.out.println("info - no class implementing " + 
+			   "BundleActivator found");
+	break;
+      case 1:
+	{
+	  String clazz = (String)activatorSet.iterator().next();
+	  String clazz0 = proj.getProperty(activatorProperty);
+	  if(clazz0 == null || "".equals(clazz0)) {
+	    // System.out.println("set activator " + activatorProperty + "=" + clazz);
+	  } else {
+	    if(!clazz.equals(clazz0)) {
+	      System.out.println("*** Warning - the class found implementing " + 
+				 " BundleActivator '" + clazz + "' " + 
+				 " does not match the one set as " + 
+				 activatorProperty + "=" + clazz0);
+	    } else {
+	      if(bDebug) {
+		System.out.println("correct activator " + 
+				   activatorProperty + "=" + clazz0);
+	      }
+	    }
+	  }
+	  proj.setNewProperty(activatorProperty, clazz);
+	}
+	break;
+      default:
+	System.out.println("*** Warning - more than one class " + 
+			   "implementing BundleActivator found:");
+	for(Iterator it = activatorSet.iterator(); it.hasNext();) {
+	  System.out.println(" " + it.next());
+	}
+	break;
+	
+      }
+    }
+
+    Set foundationMissing = new TreeSet();
+    Set minimumMissing    = new TreeSet();
+    Set smfMissing        = new TreeSet();
+
+
+    for(Iterator it = classSet.iterator(); it.hasNext();) {
+      String s = (String)it.next();
+      if(s.endsWith("[]")) {
+      } else {
+	if(!ownClasses.contains(s)) {
+	  if(bPrintClasses) {
+	    System.out.println(s);
+	  } 
+	  if(!EE.isFoundation(s)) {
+	    if(!isImported(s)) {
+	      foundationMissing.add(s);
+	    }
+	  }
+	  if(!EE.isMinimum(s)) {
+	    if(!isImported(s)) {
+	      minimumMissing.add(s);
+	    }
+	  }
+	  if(!EE.isSMF(s)) {
+	    if(!isImported(s)) {
+	      smfMissing.add(s);
+	    }
+	  }
+	}
+      }
+    }
+
+    if(bCheckFoundationEE) {
+      if(foundationMissing.size() > 0) {
+	System.out.println("Missing " + foundationMissing.size() + 
+			   " classes from foundation profile");
+      } else {
+	System.out.println("Passes foundation EE");
+      }
+      for(Iterator it = foundationMissing.iterator(); it.hasNext();) {
+	String s = (String)it.next();
+	System.out.println("Not in foundation: " + s);
+      }
+    }
+
+    if(bCheckMinimumEE) {
+      if(minimumMissing.size() > 0) {
+	System.out.println("Missing " + minimumMissing.size() + 
+			   " classes from minimum profile");
+      } else {
+	System.out.println("Passes minimum EE");
+      }
+      for(Iterator it = minimumMissing.iterator(); it.hasNext();) {
+	String s = (String)it.next();
+	System.out.println("Not in minimum: " + s);
+      }
+    }
+
+    if(bCheckSMFEE) {
+      if(smfMissing.size() > 0) {
+	System.out.println("Missing " + smfMissing.size() + 
+			   " classes from SMF profile");
+      } else {
+	System.out.println("Passes SMF EE");
+      }
+      for(Iterator it = smfMissing.iterator(); it.hasNext();) {
+	String s = (String)it.next();
+	System.out.println("Not in SMF: " + s);
+      }
+    }
+  }
+
+  
+
+
+  /**
+   * Analyze a file by checking its suffix and delegate to 
+   * <tt>analyzeClass</tt>, <tt>analyzeJava</tt> etc
+   */
+  protected void analyze(File file) throws BuildException {
+    if(file.getName().endsWith(".class")) {
+      analyzeClass(file);
+    } else if(file.getName().endsWith(".java")) {
+      analyzeJava(file);
+    } else {
+      // Just ignore all other files
+    }
+  }
+
+
+  protected void addExportedPackageString(String name) {
+    if(name == null || "".equals(name)) {
+      return;
+    }
+
+    if(bDebug) {
+      System.out.println(" package " + name);
+    }
+    exportSet.add(name);
+  }
+
+
+  protected void addActivatorString(String s) {
+    activatorSet.add(s);
+  }
+
+  /**
+   * Add a type's package name to the list of imported packages.
+   *
+   * @param t Type of an object.
+   */
+  protected void addImportedType(Type t) {
+    if(t instanceof BasicType) {
+      // Ignore all basic types
+    } else {
+      addImportedString(t.toString());
+    }
+  }
+
+  /**
+   * Add a class' package name to the list of imported packages.
+   *
+   * @param className Class name of an object. The class name is stripped
+   *                  from the part after the last '.' and added to set
+   *                  of imported packages, if its not one of the standard
+   *                  packages. Primitive class names are ignore. 
+   */
+  protected void addImportedString(String className) {
+
+    if(className == null) {
+      return;
+    }
+
+    String name = packageName(className);
+
+    if("".equals(name)) {
+      return;
+    }
+
+    // only add packages defined outside this set of files
+    if(!exportSet.contains(name)) {
+
+      // ...and only add non-std packages
+      if(!isStdImport(name)) {
+	importSet.add(name);
+      }
+    }
+
+    classSet.add(className);
+  }
+
+  boolean isImported(String className) {
+    for(Iterator it = importSet.iterator(); it.hasNext(); ) {
+      String pkg = (String)it.next();
+      if(className.startsWith(pkg)) {
+	String rest = className.substring(pkg.length() + 1);
+	if(-1 == rest.indexOf(".")) {
+	  return true;
+	}
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Check if package is included in prefix list of standard packages.
+   *
+   * @return <tt>true</tt> if name is prefixed with any of the elements in
+   *         <tt>stdImports</tt> set, <tt>false</tt> otherwise.
+   */
+  protected boolean isStdImport(String name) {
+    for(Iterator it = stdImports.iterator(); it.hasNext();) {
+      String s = (String)it.next();
+      if(name.startsWith(s)) {
+	return true;
+      }
+    }
+    return false;
+  }
+
+
+  protected void analyzeJar(File file) throws BuildException {
+    throw new BuildException("Jar file analyzing not yet supported");
+  }
+
+  protected void analyzeClass(File file) throws BuildException {
+    if(bDebug) {
+      System.out.println("Analyze class file " + file.getAbsolutePath());
+    }
+
+    try {
+      ClassParser        parser   = new ClassParser(file.getAbsolutePath());
+      final JavaClass    clazz    = parser.parse();
+      final ConstantPool constant_pool = clazz.getConstantPool();
+
+      ownClasses.add(clazz.getClassName());
+      
+      DescendingVisitor v = new DescendingVisitor(clazz, new EmptyVisitor() {
+	  
+
+	  // Delegate all ConstantCP calls to visitConstantCP()
+	  public void visitConstantFieldRef(ConstantFieldref obj) {
+	    visitConstantCP(obj);
+	  }
+	  public void visitConstantMethodref(ConstantMethodref obj) {
+	    visitConstantCP(obj);
+	  }
+	  public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj) {
+	    visitConstantCP(obj);
+	  }
+
+	  // Extract information from method calls found
+	  // in constant table
+	  // This information is used to get all imported packages
+	  private void visitConstantCP(ConstantCP obj) {
+
+	    // This is amazingly complex - isn't there any 
+	    // easier way to get the referenced class names???
+
+	    int signature_ix = obj.getNameAndTypeIndex();
+	    int class_ix     = obj.getClassIndex();
+	    
+	    ConstantNameAndType c = (ConstantNameAndType)
+	      constant_pool.getConstant(signature_ix, 
+					Constants.CONSTANT_NameAndType);
+	    
+	    ConstantClass cc = 	(ConstantClass)
+	      constant_pool.getConstant(class_ix, 
+					Constants.CONSTANT_Class);
+	    
+	    // Whoa!! This is the name of the called object's class
+	    String objType = Utility.compactClassName(((ConstantUtf8)constant_pool.getConstant(cc.getNameIndex(), Constants.CONSTANT_Utf8)).getBytes(), false);
+	    
+
+	    String sig  = c.getSignature(constant_pool);
+	    String name = c.getName(constant_pool);
+
+	    String   retType   = Utility.methodSignatureReturnType(sig);
+	    String[] argTypes  = Utility.methodSignatureArgumentTypes(sig);
+
+	    for(int i = 0; i < argTypes.length; i++) {
+	      addImportedString(argTypes[i]);
+	    }
+
+	    addImportedString(retType);
+	    addImportedString(objType);
+	  }
+
+
+	  // This information is used to get all imported packages, 
+	  // as well as extracting the class package name for exported
+	  // packages.
+	  public void visitJavaClass(JavaClass obj) { 
+	    
+	    addExportedPackageString(obj.getPackageName());
+
+	    addImportedString(obj.getSuperclassName());
+	    
+	    // Scan all implemented interfaces to find
+	    // candidates for the activator AND to find
+	    // all referenced packages.
+	    String[] interfaces = obj.getInterfaceNames();
+	    for(int i = 0; i < interfaces.length; i++) {
+	      addImportedString(interfaces[i]);
+	      if("org.osgi.framework.BundleActivator".equals(interfaces[i])) {
+		addActivatorString(obj.getClassName());
+	      }
+	    }
+	  } 
+	  
+	  // hmmm...this should already be found in constant pool
+	  public void visitLocalVariable(LocalVariable obj) { 
+	    Type type = Type.getType(obj.getSignature());
+
+	    addImportedType(type);
+	  } 
+	  
+
+	  // this too should be found in constant pool. 
+	  // But I'm really unsure.
+	  public void visitMethod(Method obj) { 
+	    addImportedType(obj.getReturnType());
+
+	    Type[] argv = obj.getArgumentTypes();
+
+	    for(int i = 0; i < argv.length; i++) {
+	      addImportedType(argv[i]);
+	    }	    
+	  } 
+
+	});
+
+      // Run the scanner on the loaded class
+      v.visit();
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      throw new BuildException("Failed to parse .class file " + 
+			       file + ", exception=" + e);
+    }
+  }
+
+  /**
+   * Analyze java source file by reading line by line and looking
+   * for keywords such as "import", "package".
+   *
+   * <p>
+   * <b>Note</b>: This code does not attempt to find any class implementing
+   * <tt>BundleActivator</tt>
+   */
+  protected void analyzeJava(File file) throws BuildException {
+
+    if(bDebug) {
+      System.out.println("Analyze java file " + file.getAbsolutePath());
+    }
+
+    BufferedReader reader = null;
+
+    try {
+      String line;
+      int    lineNo = 0;
+
+      reader = new BufferedReader(new FileReader(file));
+
+      while(null != (line = reader.readLine())) {
+	lineNo++;
+	line = line.replace(';', ' ').replace('\t', ' ').trim();
+	
+	if(line.startsWith("package")) {
+	  Vector v = StringUtils.split(line, ' ');
+	  if(v.size() > 1 && "package".equals(v.elementAt(0))) {
+	    String name = (String)v.elementAt(1);
+	    addExportedPackageString(name);
+	  }
+	}
+	if(line.startsWith("import")) {
+	  Vector v = StringUtils.split(line, ' ');
+	  if(v.size() > 1 && "import".equals(v.elementAt(0))) {
+	    String name = (String)v.elementAt(1);
+	    addImportedString(name);
+	  }
+	}
+      }
+    } catch (Exception e) {
+      throw new BuildException("Failed to scan " + file + ", err=" + e);
+    } finally {
+      if(reader != null) {
+	try { reader.close(); } catch (Exception ignored) { }
+      }
+    }
+  }
+
+  /**
+   * Get package name of class string representation.
+   */
+  static String packageName(String s) {
+    s = s.trim();
+    int ix = s.lastIndexOf('.');
+    if(ix != -1) {
+      s = s.substring(0, ix);
+    } else {
+      s = "";
+    }
+    return s;
+  }
+
+  /**
+   * Convert Set elements to a string.
+   *
+   * @param separator String to use as sperator between elements.
+   */
+  static protected String toString(Set set, String separator) {
+    StringBuffer sb = new StringBuffer();
+
+    for(Iterator it = set.iterator(); it.hasNext(); ) {
+      String name = (String)it.next();
+      sb.append(name);
+      if(it.hasNext()) {
+	sb.append(separator);
+      }
+    }
+    return sb.toString();
+  }
+}

Added: cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleManifestTask.java
URL: http://svn.apache.org/viewcvs/cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleManifestTask.java?rev=189461&view=auto
==============================================================================
--- cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleManifestTask.java (added)
+++ cocoon/whiteboard/osgi/ant/src/org/knopflerfish/ant/taskdefs/bundle/BundleManifestTask.java Tue Jun  7 15:32:48 2005
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2003, KNOPFLERFISH project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ * 
+ * - Redistributions of source code must retain the above copyright notice, 
+ *   this list of conditions and the following disclaimer. 
+ * 
+ * - Redistributions in binary form must reproduce the above copyright notice, 
+ *   this list of conditions and the following disclaimer in the documentation 
+ *   and/or other materials provided with the distribution. 
+ * 
+ * - Neither the name of the KNOPFLERFISH project nor the names of its 
+ *   contributors may be used to endorse or promote products derived 
+ *   from this software without specific prior written permission. 
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.knopflerfish.ant.taskdefs.bundle;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Manifest;
+import org.apache.tools.ant.taskdefs.ManifestTask;
+import org.apache.tools.ant.taskdefs.ManifestException;
+
+
+/**
+ * Adaption of std Manifest task which ignores attributes
+ * with special empty values.
+ *
+ * <p>
+ * Any attrubute with the value
+ * <pre>
+ * [bundle.emptystring]
+ * </pre>
+ * will <b>not</b> be stored in the manifest at all.
+ * </p>
+ */
+public class BundleManifestTask extends ManifestTask {
+  public BundleManifestTask() {
+    super();
+   }
+
+  /**
+   * Override <tt>ManifestTask.addConfiguredAttribute</tt> with
+   * version which checks if the attribute should be stored.
+   */
+  public void addConfiguredAttribute(Manifest.Attribute attribute)
+    throws ManifestException {
+    if("[bundle.emptystring]".equals(attribute.getValue())) {
+      return;
+    }
+    super.addConfiguredAttribute(attribute);
+  }
+  
+}