1 19 20 package edu.umd.cs.findbugs.tools.eclipse; 21 22 import java.io.BufferedInputStream ; 23 import java.io.BufferedReader ; 24 import java.io.File ; 25 import java.io.FileFilter ; 26 import java.io.FileInputStream ; 27 import java.io.FileReader ; 28 import java.io.IOException ; 29 import java.io.Reader ; 30 import java.util.HashMap ; 31 import java.util.Iterator ; 32 import java.util.LinkedList ; 33 import java.util.List ; 34 import java.util.Map ; 35 import java.util.StringTokenizer ; 36 import java.util.jar.Attributes ; 37 import java.util.jar.Manifest ; 38 import java.util.regex.Matcher ; 39 import java.util.regex.Pattern ; 40 41 import org.dom4j.Document; 42 import org.dom4j.DocumentException; 43 import org.dom4j.Node; 44 import org.dom4j.io.SAXReader; 45 46 59 public class EclipseClasspath { 60 61 public static class EclipseClasspathException extends Exception { 62 65 private static final long serialVersionUID = 1L; 66 67 public EclipseClasspathException(String msg) { 68 super(msg); 69 } 70 71 public EclipseClasspathException(String msg, Throwable e) { 72 super(msg, e); 73 } 74 } 75 76 86 private static class EclipseXMLReader extends Reader { 87 private BufferedReader reader; 88 private LinkedList <String > lineList; 89 90 public EclipseXMLReader(Reader reader) { 91 this.reader = new BufferedReader (reader); 92 this.lineList = new LinkedList <String >(); 93 } 94 95 @Override 96 public int read(char[] cbuf, int off, int len) throws IOException { 97 if (!fill()) 98 return -1; 99 String line = lineList.getFirst(); 100 if (len > line.length()) 101 len = line.length(); 102 for (int i = 0; i < len; ++i) 103 cbuf[i+off] = line.charAt(i); 104 if (len == line.length()) 105 lineList.removeFirst(); 106 else 107 lineList.set(0, line.substring(len)); 108 return len; 109 } 110 111 @Override 112 public void close() throws IOException { 113 reader.close(); 114 } 115 116 private boolean fill() throws IOException { 117 if (!lineList.isEmpty()) 118 return true; 119 120 String line; 121 do { 122 line = reader.readLine(); 123 if (line == null) 124 return false; 125 } while (isIllegal(line)); 126 lineList.add(line+"\n"); 127 return true; 128 } 129 130 private boolean isIllegal(String line) { 131 return line.startsWith("<?eclipse"); 132 } 133 } 134 135 private class Plugin { 136 private String directory; 137 private boolean isDependent; 138 private String pluginId; 139 private String pluginVersion; 140 private List <String > requiredPluginIdList; 141 private List <String > exportedLibraryList; 142 143 public Plugin(String directory, boolean isDependent) 144 throws DocumentException, EclipseClasspathException, IOException { 145 this.directory = directory; 146 this.isDependent = isDependent; 147 this.requiredPluginIdList = new LinkedList <String >(); 148 this.exportedLibraryList = new LinkedList <String >(); 149 150 153 boolean oldStyle = false; 154 155 Document document = null; 156 File pluginDescriptorFile = new File (directory + File.separator + "plugin.xml"); 157 if (pluginDescriptorFile.isFile()) { 158 SAXReader reader = new SAXReader(); 159 document = reader.read(new EclipseXMLReader(new FileReader (pluginDescriptorFile))); 160 161 Node plugin = document.selectSingleNode("/plugin"); 162 if (plugin == null) 163 throw new EclipseClasspathException("No plugin node in plugin descriptor"); 164 165 oldStyle = !plugin.valueOf("@id").equals(""); 166 } 167 168 170 if (oldStyle) { 171 parseOldPluginDescriptor(directory, document, isDependent); 172 } else { 173 parseNewPluginDescriptor(directory, isDependent); 174 } 175 } 176 177 public String getDirectory() { 178 return directory; 179 } 180 181 public boolean isDependent() { 182 return isDependent; 183 } 184 185 public String getId() { 186 return pluginId; 187 } 188 189 public String getVersion() { 190 return pluginVersion; 191 } 192 193 public Iterator <String > requiredPluginIdIterator() { 194 return requiredPluginIdList.iterator(); 195 } 196 197 public Iterator <String > exportedLibraryIterator() { 198 return exportedLibraryList.iterator(); 199 } 200 201 private void parseOldPluginDescriptor(String directory, Document document, boolean isDependent) 202 throws DocumentException, EclipseClasspathException { 203 206 Node plugin = document.selectSingleNode("/plugin"); 207 208 pluginId = plugin.valueOf("@id"); 209 pluginVersion = plugin.valueOf("@version"); 211 if (pluginVersion.equals("")) 212 throw new EclipseClasspathException("Cannot determine plugin version"); 213 214 List <Node> requiredPluginNodeList = (List <Node>) document.selectNodes("/plugin/requires/import"); 216 for (Node node : requiredPluginNodeList) { 217 String requiredPluginId = node.valueOf("@plugin"); 218 if (requiredPluginId.equals("")) 219 throw new EclipseClasspathException("Import has no plugin id"); 220 requiredPluginIdList.add(requiredPluginId); 222 } 223 224 List <Node> exportedLibraryNodeList = (List <Node>) document.selectNodes("/plugin/runtime/library"); 226 for (Node node : exportedLibraryNodeList) { 227 String jarName = node.valueOf("@name"); 228 if (jarName.equals("")) 229 throw new EclipseClasspathException("Could not get name of exported library"); 230 231 jarName = replaceSpecial(jarName); 232 File jarFile = new File (jarName); 233 if (!jarFile.isAbsolute()) { 234 jarFile = new File (directory, jarName); 236 } 237 exportedLibraryList.add(jarFile.getPath()); 238 } 239 } 240 241 private void parseNewPluginDescriptor(String directory, boolean isDependent) 242 throws DocumentException, EclipseClasspathException { 243 245 BufferedInputStream in = null; 246 try { 247 String manifestFileName = directory + File.separator + "META-INF/MANIFEST.MF"; 248 in = new BufferedInputStream (new FileInputStream (manifestFileName)); 249 250 Manifest manifest = new Manifest (in); 251 Attributes attributes = manifest.getMainAttributes(); 252 253 pluginId = attributes.getValue("Bundle-SymbolicName"); 255 if (pluginId == null) 256 throw new EclipseClasspathException("Missing Bundle-SymbolicName in " + manifestFileName); 257 258 pluginId = stripSemiPart(pluginId); 259 260 pluginVersion = attributes.getValue("Bundle-Version"); 262 if (pluginVersion == null) 263 throw new EclipseClasspathException("Missing Bundle-Version in " + manifestFileName); 264 265 String required = attributes.getValue("Require-Bundle"); 267 if (required != null) { 268 StringTokenizer tok = new StringTokenizer (required, ","); 269 while (tok.hasMoreTokens()) { 270 String requiredPlugin = tok.nextToken(); 271 requiredPlugin = requiredPlugin.trim(); 272 requiredPlugin = stripSemiPart(requiredPlugin); 273 requiredPluginIdList.add(requiredPlugin); 275 } 276 } 277 278 String exported = attributes.getValue("Bundle-ClassPath"); 280 if (exported != null) { 281 StringTokenizer tok2 = new StringTokenizer (exported, ","); 282 while (tok2.hasMoreTokens()) { 283 String jar = tok2.nextToken(); 284 jar = jar.trim(); 285 jar = stripSemiPart(jar); 286 exportedLibraryList.add(directory + File.separator + jar); 287 } 288 } 289 290 } catch (IOException e) { 291 throw new EclipseClasspathException("Could not parse Eclipse 3.0 plugin in " + directory, e); 292 } finally { 293 try { 294 if (in != null) 295 in.close(); 296 } catch (IOException e) { 297 } 299 } 300 } 301 302 private String stripSemiPart(String s) { 303 int semi = s.indexOf(';'); 304 if (semi >= 0) 305 s = s.substring(0, semi); 306 return s; 307 } 308 } 309 310 private static final Pattern pluginDirPattern = Pattern.compile("^(\\w+(\\.\\w+)*)_(\\w+(\\.\\w+)*)$"); 311 private static final int PLUGIN_ID_GROUP = 1; 312 313 private String eclipseDir; 314 private String rootPluginDir; 315 private Map <String , File > pluginDirectoryMap; 316 private Map <String , String > varMap; 317 private Plugin rootPlugin; 318 private List <String > importList; 319 320 public EclipseClasspath(String eclipseDir, String rootPluginDir) { 321 this.eclipseDir = eclipseDir; 322 this.rootPluginDir = rootPluginDir; 323 this.pluginDirectoryMap = new HashMap <String , File >(); 324 this.varMap = new HashMap <String , String >(); 325 } 326 327 public void addRequiredPlugin(String pluginId, String pluginDir) { 328 pluginDirectoryMap.put(pluginId, new File (pluginDir)); 329 } 330 331 private static class WorkListItem { 332 private String directory; 333 private boolean isDependent; 334 335 public WorkListItem(String directory, boolean isDependent) { 336 this.directory = directory; 337 this.isDependent = isDependent; 338 } 339 340 public String getDirectory() { return directory; } 341 public boolean isDependent() { return isDependent; } 342 } 343 344 public EclipseClasspath execute() throws EclipseClasspathException, DocumentException, IOException { 345 346 File pluginDir = new File (eclipseDir, "plugins"); 348 File [] dirList = pluginDir.listFiles(new FileFilter () { 349 public boolean accept(File pathname) { 350 return pathname.isDirectory(); 351 } 352 }); 353 if (dirList == null) 354 throw new EclipseClasspathException("Could not list plugins in directory " + pluginDir); 355 for (File aDirList : dirList) { 356 String dirName = aDirList.getName(); 357 String pluginId = getPluginId(dirName); 358 if (pluginId != null) { 359 pluginDirectoryMap.put(pluginId, aDirList); 361 362 if (pluginId.startsWith("org.eclipse.swt.")) { 364 String ws = pluginId.substring("org.eclipse.swt.".length()); 365 if (ws.equals("gtk64")) 366 ws = "gtk"; 367 varMap.put("ws", new File (aDirList + File.separator + "ws" + File.separator + ws).getPath().replace('\\', '/')); 368 } 369 } 370 } 371 372 Map <String , Plugin> requiredPluginMap = new HashMap <String , Plugin>(); 373 374 LinkedList <WorkListItem> workList = new LinkedList <WorkListItem>(); 375 workList.add(new WorkListItem(rootPluginDir, false)); 376 377 while (!workList.isEmpty()) { 378 WorkListItem item = workList.removeFirst(); 379 380 Plugin plugin = new Plugin(item.getDirectory(), item.isDependent()); 381 requiredPluginMap.put(plugin.getId(), plugin); 382 383 if (!plugin.isDependent()) { 384 if (rootPlugin != null) throw new IllegalStateException ("multiple root plugins"); 385 this.rootPlugin = plugin; 386 } 387 388 for (Iterator <String > i = plugin.requiredPluginIdIterator(); i.hasNext(); ) { 390 String requiredPluginId = i.next(); 391 if (requiredPluginMap.get(requiredPluginId) == null) { 392 File requiredPluginDir = pluginDirectoryMap.get(requiredPluginId); 394 if (requiredPluginDir == null) 395 throw new EclipseClasspathException("Unable to find plugin " + requiredPluginId); 396 workList.add(new WorkListItem(requiredPluginDir.getPath(), true)); 397 } 398 } 399 } 400 401 403 importList = new LinkedList <String >(); 404 for (Plugin plugin : requiredPluginMap.values()) { 405 if (plugin.isDependent()) { 406 for (Iterator <String > j = plugin.exportedLibraryIterator(); j.hasNext();) { 407 importList.add(j.next()); 409 } 410 } 411 } 412 413 return this; 414 } 415 416 public Iterator <String > importListIterator() { 417 return importList.iterator(); 418 } 419 420 public String getClasspath() { 421 if (importList == null) throw new IllegalStateException ("execute() has not been called"); 422 423 StringBuffer buf = new StringBuffer (); 424 boolean first = true; 425 426 Iterator <String > i = importListIterator(); 427 while (i.hasNext()) { 428 if (first) 429 first = false; 430 else 431 buf.append(File.pathSeparator); 432 433 buf.append(i.next()); 434 } 435 436 String s = buf.toString(); 439 s = s.replace('\\', '/'); 440 441 return s; 442 } 443 444 public Plugin getRootPlugin() { 445 return rootPlugin; 446 } 447 448 453 private String getPluginId(String dirName) { 454 Matcher m = pluginDirPattern.matcher(dirName); 455 return m.matches() ? m.group(PLUGIN_ID_GROUP) : null; 456 } 457 458 463 private String replaceSpecial(String value) { 464 if (!varMap.isEmpty()) { 465 for (String key : varMap.keySet()) { 466 String replace = varMap.get(key); 467 Pattern pat = Pattern.compile("\\$\\Q" + key + "\\E\\$"); 468 Matcher m = pat.matcher(value); 469 StringBuffer buf = new StringBuffer (); 470 471 while (m.find()) 472 m.appendReplacement(buf, replace); 473 m.appendTail(buf); 474 475 value = buf.toString(); 476 } 477 } 478 return value; 479 } 480 481 public static void main(String [] argv) throws Exception { 482 if (argv.length < 2 || (argv.length % 2 != 0)) { 483 System.err.println("Usage: " + EclipseClasspath.class.getName() + 484 " <eclipse dir> <root plugin directory> [<required plugin id> <required plugin dir> ...]"); 485 System.exit(1); 486 } 487 488 EclipseClasspath ec = new EclipseClasspath(argv[0], argv[1]); 489 for (int i = 2; i < argv.length; i += 2) { 490 ec.addRequiredPlugin(argv[i], new File (argv[i+1]).getAbsolutePath()); 491 } 492 493 ec.execute(); 497 System.out.println("plugin.build.classpath=" + ec.getClasspath()); 498 System.out.println("plugin.id=" + ec.getRootPlugin().getId()); 499 System.out.println("plugin.version=" + ec.getRootPlugin().getVersion()); 500 } 501 } 502 503 | Popular Tags |