1 19 20 package edu.umd.cs.findbugs; 21 22 import java.net.URL ; 23 import java.net.URLClassLoader ; 24 import java.util.ArrayList ; 25 import java.util.HashSet ; 26 import java.util.List ; 27 import java.util.Locale ; 28 import java.util.Set ; 29 30 import org.dom4j.Document; 31 import org.dom4j.DocumentException; 32 import org.dom4j.Element; 33 import org.dom4j.Node; 34 import org.dom4j.io.SAXReader; 35 36 import edu.umd.cs.findbugs.plan.ByInterfaceDetectorFactorySelector; 37 import edu.umd.cs.findbugs.plan.DetectorFactorySelector; 38 import edu.umd.cs.findbugs.plan.DetectorOrderingConstraint; 39 import edu.umd.cs.findbugs.plan.ReportingDetectorFactorySelector; 40 import edu.umd.cs.findbugs.plan.SingleDetectorFactorySelector; 41 42 60 public class PluginLoader extends URLClassLoader { 61 62 private static final boolean DEBUG = SystemProperties.getBoolean("findbugs.debug.PluginLoader"); 63 64 private static int nextUnknownId; 67 68 private Plugin plugin; 70 71 77 public PluginLoader(URL url) throws PluginException { 78 super(new URL []{url}); 79 init(); 80 } 81 82 88 public PluginLoader(URL url, ClassLoader parent) throws PluginException { 89 super(new URL []{url}, parent); 90 init(); 91 } 92 93 97 public Plugin getPlugin() throws PluginException { 98 if (plugin == null) 99 init(); 100 return plugin; 101 } 102 103 private void init() throws PluginException { 104 Document pluginDescriptor; 107 108 String pluginId; 110 111 ArrayList <Document> messageCollectionList = new ArrayList <Document>(); 113 114 try { 116 URL descriptorURL = findResource("findbugs.xml"); 117 if (descriptorURL == null) 118 throw new PluginException("Couldn't find \"findbugs.xml\" in plugin"); 119 120 SAXReader reader = new SAXReader(); 121 pluginDescriptor = reader.read(descriptorURL); 122 } catch (DocumentException e) { 123 throw new PluginException("Couldn't parse \"findbugs.xml\"", e); 124 } 125 126 pluginId = pluginDescriptor.valueOf("/FindbugsPlugin/@pluginid"); 128 if (pluginId.equals("")) { 129 synchronized (PluginLoader.class) { 130 pluginId = "plugin" + nextUnknownId++; 131 } 132 } 133 134 String defaultEnabled = pluginDescriptor.valueOf("/FindbugsPlugin/@defaultenabled"); 138 boolean pluginEnabled = defaultEnabled.equals("") || Boolean.valueOf(defaultEnabled).booleanValue(); 139 140 try { 142 Locale locale = I18N.defaultLocale; 144 String language = locale.getLanguage(); 145 String country = locale.getCountry(); 146 147 if (country != null) 148 addCollection(messageCollectionList, "messages_" + language + "_" + country + ".xml"); 149 addCollection(messageCollectionList, "messages_" + language + ".xml"); 150 addCollection(messageCollectionList, "messages.xml"); 151 } catch (DocumentException e) { 152 e.printStackTrace(); 153 throw new PluginException("Couldn't parse \"messages.xml\"", e); 154 } 155 156 Plugin plugin = new Plugin(pluginId); 159 plugin.setEnabled(pluginEnabled); 160 161 String provider = pluginDescriptor.valueOf("/FindbugsPlugin/@provider"); 163 if (!provider.equals("")) 164 plugin.setProvider(provider); 165 String website = pluginDescriptor.valueOf("/FindbugsPlugin/@website"); 166 if (!website.equals("")) 167 plugin.setWebsite(website); 168 169 Node pluginShortDesc = null; 171 try { 172 pluginShortDesc = findMessageNode( 173 messageCollectionList, 174 "/MessageCollection/Plugin/ShortDescription", 175 "no plugin description"); 176 } catch (PluginException e) { 177 } 179 if (pluginShortDesc != null) { 180 plugin.setShortDescription(pluginShortDesc.getText()); 181 } 182 183 try { 185 List <Node> detectorNodeList = pluginDescriptor.selectNodes("/FindbugsPlugin/Detector"); 186 int detectorCount = 0; 187 for (Node detectorNode : detectorNodeList) { 188 String className = detectorNode.valueOf("@class"); 189 String speed = detectorNode.valueOf("@speed"); 190 String disabled = detectorNode.valueOf("@disabled"); 191 String reports = detectorNode.valueOf("@reports"); 192 String requireJRE = detectorNode.valueOf("@requirejre"); 193 String hidden = detectorNode.valueOf("@hidden"); 194 195 197 Class <?> detectorClass = loadClass(className); 199 if (!Detector.class.isAssignableFrom(detectorClass) 200 && !Detector2.class.isAssignableFrom(detectorClass)) 201 throw new PluginException("Class " + className + " does not implement Detector or Detector2"); 202 DetectorFactory factory = new DetectorFactory( 203 plugin, 204 detectorClass, !disabled.equals("true"), 205 speed, reports, requireJRE); 206 if (Boolean.valueOf(hidden).booleanValue()) 207 factory.setHidden(true); 208 factory.setPositionSpecifiedInPluginDescriptor(detectorCount++); 209 plugin.addDetectorFactory(factory); 210 211 Node node = findMessageNode(messageCollectionList, 214 "/MessageCollection/Detector[@class='" + className + "']/Details", 215 "Missing Detector description for detector " + className); 216 217 Element details = (Element) node; 218 String detailHTML = details.getText(); 219 StringBuffer buf = new StringBuffer (); 220 buf.append("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n"); 221 buf.append("<HTML><HEAD><TITLE>Detector Description</TITLE></HEAD><BODY>\n"); 222 buf.append(detailHTML); 223 buf.append("</BODY></HTML>\n"); 224 factory.setDetailHTML(buf.toString()); 225 } 226 } catch (ClassNotFoundException e) { 227 throw new PluginException("Could not instantiate detector class: " + e, e); 228 } 229 230 Node orderingConstraintsNode = 232 pluginDescriptor.selectSingleNode("/FindbugsPlugin/OrderingConstraints"); 233 if (orderingConstraintsNode != null) { 234 for (Element constraintElement : (List <Element>) orderingConstraintsNode.selectNodes("./SplitPass|./WithinPass")) 236 { 237 DetectorFactorySelector earlierSelector = getConstraintSelector( 240 constraintElement, plugin, "Earlier", "EarlierCategory"); 241 DetectorFactorySelector laterSelector = getConstraintSelector( 242 constraintElement, plugin, "Later", "LaterCategory"); 243 244 DetectorOrderingConstraint constraint = new DetectorOrderingConstraint( 246 earlierSelector, laterSelector); 247 248 if (constraintElement.getName().equals("SplitPass")) 250 plugin.addInterPassOrderingConstraint(constraint); 251 else 252 plugin.addIntraPassOrderingConstraint(constraint); 253 } 254 } 255 256 I18N i18n = I18N.instance(); 258 for (Document messageCollection : messageCollectionList) { 259 List <Node> categoryNodeList = messageCollection.selectNodes("/MessageCollection/BugCategory"); 260 if (DEBUG) System.out.println("found "+categoryNodeList.size()+" categories in "+messageCollection.getName()); 261 for (Node categoryNode : categoryNodeList) { 262 String key = categoryNode.valueOf("@category"); 263 if (key.equals("")) 264 throw new PluginException("BugCategory element with missing category attribute"); 265 String shortDesc = getChildText(categoryNode, "Description"); 266 BugCategory bc = new BugCategory(key, shortDesc); 267 boolean b = i18n.registerBugCategory(key, bc); 268 if (DEBUG) System.out.println(b 269 ? "category "+key+" -> "+shortDesc 270 : "rejected \""+shortDesc+"\" for category "+key+": "+i18n.getBugCategoryDescription(key)); 271 274 if (!b) bc = i18n.getBugCategory(key); try { 276 String abbrev = getChildText(categoryNode, "Abbreviation"); 277 if (bc.getAbbrev() == null) { 278 bc.setAbbrev(abbrev); 279 if (DEBUG) System.out.println("category "+key+" abbrev -> "+abbrev); 280 } 281 else if (DEBUG) System.out.println("rejected abbrev '"+abbrev+"' for category "+key+": "+bc.getAbbrev()); 282 } catch (PluginException pe) { 283 if (DEBUG) System.out.println("missing Abbreviation for category "+key+"/"+shortDesc); 284 } 286 try { 287 String details = getChildText(categoryNode, "Details"); 288 if (bc.getDetailText() == null) { 289 bc.setDetailText(details); 290 if (DEBUG) System.out.println("category "+key+" details -> "+details); 291 } 292 else if (DEBUG) System.out.println("rejected details ["+details+"] for category "+key+": ["+bc.getDetailText()+']'); 293 } catch (PluginException pe) { 294 } 296 } 297 } 298 299 List <Node> bugPatternNodeList = pluginDescriptor.selectNodes("/FindbugsPlugin/BugPattern"); 301 for (Node bugPatternNode : bugPatternNodeList) { 302 String type = bugPatternNode.valueOf("@type"); 303 String abbrev = bugPatternNode.valueOf("@abbrev"); 304 String category = bugPatternNode.valueOf("@category"); 305 String experimental = bugPatternNode.valueOf("@experimental"); 306 307 String query = "/MessageCollection/BugPattern[@type='" + type + "']"; 309 Node messageNode = findMessageNode(messageCollectionList, query, 310 "messages.xml missing BugPattern element for type " + type); 311 312 String shortDesc = getChildText(messageNode, "ShortDescription"); 313 String longDesc = getChildText(messageNode, "LongDescription"); 314 String detailText = getChildText(messageNode, "Details"); 315 316 BugPattern bugPattern = new BugPattern(type, abbrev, category, 317 Boolean.valueOf(experimental).booleanValue(), 318 shortDesc, longDesc, detailText); 319 plugin.addBugPattern(bugPattern); 320 boolean unknownCategory = (null == i18n.getBugCategory(category)); 321 if (unknownCategory) { 322 i18n.registerBugCategory(category, new BugCategory(category, category)); 323 if (DEBUG) System.out.println("Category "+category+" (of BugPattern " 325 +type+") has no description in messages*.xml"); 326 } 328 } 329 330 Set <String > definedBugCodes = new HashSet <String >(); 332 for (Document messageCollection : messageCollectionList) { 333 List <Node> bugCodeNodeList = messageCollection.selectNodes("/MessageCollection/BugCode"); 334 for (Node bugCodeNode : bugCodeNodeList) { 335 String abbrev = bugCodeNode.valueOf("@abbrev"); 336 if (abbrev.equals("")) 337 throw new PluginException("BugCode element with missing abbrev attribute"); 338 if (definedBugCodes.contains(abbrev)) 339 continue; 340 String description = bugCodeNode.getText(); 341 BugCode bugCode = new BugCode(abbrev, description); 342 plugin.addBugCode(bugCode); 343 definedBugCodes.add(abbrev); 344 } 345 346 } 347 348 this.plugin = plugin; 352 353 } 354 355 private static DetectorFactorySelector getConstraintSelector( 356 Element constraintElement, 357 Plugin plugin, 358 String singleDetectorElementName, 359 String detectorCategoryElementName) throws PluginException { 360 Node node = constraintElement.selectSingleNode("./" + singleDetectorElementName); 361 if (node != null) { 362 String detectorClass = node.valueOf("@class"); 363 return new SingleDetectorFactorySelector(plugin, detectorClass); 364 } 365 366 node = constraintElement.selectSingleNode("./" + detectorCategoryElementName); 367 if (node != null) { 368 String categoryName = node.valueOf("@name"); 369 boolean spanPlugins = Boolean.valueOf(node.valueOf("@spanplugins")).booleanValue(); 370 if (categoryName.equals("reporting")) { 371 return new ReportingDetectorFactorySelector(spanPlugins ? null : plugin); 372 } else if (categoryName.equals("training")) { 373 return new ByInterfaceDetectorFactorySelector(spanPlugins ? null : plugin, TrainingDetector.class); 374 } else if (categoryName.equals("interprocedural")) { 375 return new ByInterfaceDetectorFactorySelector(spanPlugins ? null : plugin, InterproceduralFirstPassDetector.class); 376 } else { 377 throw new PluginException("Invalid constraint selector node"); 378 } 379 } 380 381 throw new PluginException("Invalid constraint selector node"); 382 } 383 384 private String lookupDetectorClass(Plugin plugin, String name) throws PluginException { 385 389 if (name.indexOf('.') < 0) { 390 DetectorFactory factory = plugin.getFactoryByShortName(name); 391 if (factory == null) 392 throw new PluginException("No detector found for short name '" + name + "'"); 393 name = factory.getFullName(); 394 } 395 return name; 396 } 397 398 private void addCollection(List <Document> messageCollectionList, String filename) 399 throws DocumentException { 400 URL messageURL = findResource(filename); 401 if (messageURL != null) { 402 SAXReader reader = new SAXReader(); 403 Document messageCollection = reader.read(messageURL); 404 messageCollectionList.add(messageCollection); 405 } 406 } 407 408 private static Node findMessageNode(List <Document> messageCollectionList, String xpath, 409 String missingMsg) throws PluginException { 410 411 for (Document document : messageCollectionList) { 412 Node node = document.selectSingleNode(xpath); 413 if (node != null) 414 return node; 415 } 416 throw new PluginException(missingMsg); 417 } 418 419 private static String getChildText(Node node, String childName) throws PluginException { 420 Node child = node.selectSingleNode(childName); 421 if (child == null) 422 throw new PluginException("Could not find child \"" + childName + "\" for node"); 423 return child.getText(); 424 } 425 426 } 427 428 | Popular Tags |