001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd;
005
006 import java.io.IOException;
007 import java.io.InputStream;
008 import java.util.ArrayList;
009 import java.util.Iterator;
010 import java.util.List;
011 import java.util.Properties;
012 import java.util.logging.Logger;
013
014 import javax.xml.parsers.DocumentBuilder;
015 import javax.xml.parsers.DocumentBuilderFactory;
016 import javax.xml.parsers.ParserConfigurationException;
017
018 import net.sourceforge.pmd.lang.Language;
019 import net.sourceforge.pmd.lang.LanguageVersion;
020 import net.sourceforge.pmd.lang.rule.MockRule;
021 import net.sourceforge.pmd.lang.rule.RuleReference;
022 import net.sourceforge.pmd.lang.rule.properties.PropertyDescriptorFactory;
023 import net.sourceforge.pmd.util.ResourceLoader;
024 import net.sourceforge.pmd.util.StringUtil;
025
026 import org.w3c.dom.Document;
027 import org.w3c.dom.Element;
028 import org.w3c.dom.Node;
029 import org.w3c.dom.NodeList;
030 import org.xml.sax.SAXException;
031
032 /**
033 * RuleSetFactory is responsible for creating RuleSet instances from XML content.
034 * By default Rules will be loaded using the ClassLoader for this class, using
035 * the {@link RulePriority#LOW} priority, with Rule deprecation warnings off.
036 */
037 public class RuleSetFactory {
038
039 private static final Logger LOG = Logger.getLogger(RuleSetFactory.class.getName());
040
041 private ClassLoader classLoader = RuleSetFactory.class.getClassLoader();
042 private RulePriority minimumPriority = RulePriority.LOW;
043 private boolean warnDeprecated = false;
044
045 /**
046 * Set the ClassLoader to use when loading Rules.
047 *
048 * @param classLoader The ClassLoader to use.
049 */
050 public void setClassLoader(ClassLoader classLoader) {
051 this.classLoader = classLoader;
052 }
053
054 /**
055 * Set the minimum rule priority threshold for all Rules which are loaded
056 * from RuleSets via reference.
057 *
058 * @param minimumPriority The minimum priority.
059 */
060 public void setMinimumPriority(RulePriority minimumPriority) {
061 this.minimumPriority = minimumPriority;
062 }
063
064 /**
065 * Set whether warning messages should be logged for usage of deprecated Rules.
066 * @param warnDeprecated <code>true</code> to log warning messages.
067 */
068 public void setWarnDeprecated(boolean warnDeprecated) {
069 this.warnDeprecated = warnDeprecated;
070 }
071
072 /**
073 * Returns an Iterator of RuleSet objects loaded from descriptions from the
074 * "rulesets.properties" resource for each Language with Rule support.
075 *
076 * @return An Iterator of RuleSet objects.
077 */
078 public Iterator<RuleSet> getRegisteredRuleSets() throws RuleSetNotFoundException {
079 String rulesetsProperties = null;
080 try {
081 List<RuleSetReferenceId> ruleSetReferenceIds = new ArrayList<RuleSetReferenceId>();
082 for (Language language : Language.findWithRuleSupport()) {
083 Properties props = new Properties();
084 rulesetsProperties = "rulesets/" + language.getTerseName() + "/rulesets.properties";
085 props.load(ResourceLoader.loadResourceAsStream(rulesetsProperties));
086 String rulesetFilenames = props.getProperty("rulesets.filenames");
087 ruleSetReferenceIds.addAll(RuleSetReferenceId.parse(rulesetFilenames));
088 }
089 return createRuleSets(ruleSetReferenceIds).getRuleSetsIterator();
090 } catch (IOException ioe) {
091 throw new RuntimeException("Couldn't find " + rulesetsProperties
092 + "; please ensure that the rulesets directory is on the classpath. The current classpath is: "
093 + System.getProperty("java.class.path"));
094 }
095 }
096
097 /**
098 * Create a RuleSets from a comma separated list of RuleSet reference IDs. This is a
099 * convenience method which calls {@link RuleSetReferenceId#parse(String)}, and then calls
100 * {@link #createRuleSets(List)}.
101 * The currently configured ClassLoader is used.
102 *
103 * @param referenceString A comma separated list of RuleSet reference IDs.
104 * @return The new RuleSets.
105 * @throws RuleSetNotFoundException if unable to find a resource.
106 */
107 public synchronized RuleSets createRuleSets(String referenceString) throws RuleSetNotFoundException {
108 return createRuleSets(RuleSetReferenceId.parse(referenceString));
109 }
110
111 /**
112 * Create a RuleSets from a list of RuleSetReferenceIds.
113 * The currently configured ClassLoader is used.
114 *
115 * @param ruleSetReferenceIds The List of RuleSetReferenceId of the RuleSets to create.
116 * @return The new RuleSets.
117 * @throws RuleSetNotFoundException if unable to find a resource.
118 */
119 public synchronized RuleSets createRuleSets(List<RuleSetReferenceId> ruleSetReferenceIds)
120 throws RuleSetNotFoundException {
121 RuleSets ruleSets = new RuleSets();
122 for (RuleSetReferenceId ruleSetReferenceId : ruleSetReferenceIds) {
123 RuleSet ruleSet = createRuleSet(ruleSetReferenceId);
124 ruleSets.addRuleSet(ruleSet);
125 }
126 return ruleSets;
127 }
128
129 /**
130 * Create a RuleSet from a RuleSet reference ID string. This is a
131 * convenience method which calls {@link RuleSetReferenceId#parse(String)}, gets the first
132 * item in the List, and then calls {@link #createRuleSet(RuleSetReferenceId)}.
133 * The currently configured ClassLoader is used.
134 *
135 * @param referenceString A comma separated list of RuleSet reference IDs.
136 * @return A new RuleSet.
137 * @throws RuleSetNotFoundException if unable to find a resource.
138 */
139 public synchronized RuleSet createRuleSet(String referenceString) throws RuleSetNotFoundException {
140 List<RuleSetReferenceId> references = RuleSetReferenceId.parse(referenceString);
141 if (references.isEmpty()) {
142 throw new RuleSetNotFoundException("No RuleSetReferenceId can be parsed from the string: <"
143 + referenceString + ">");
144 }
145 return createRuleSet(references.get(0));
146 }
147
148 /**
149 * Create a RuleSet from a RuleSetReferenceId. Priority filtering is ignored when loading
150 * a single Rule.
151 * The currently configured ClassLoader is used.
152 *
153 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet to create.
154 * @return A new RuleSet.
155 * @throws RuleSetNotFoundException if unable to find a resource.
156 */
157 public synchronized RuleSet createRuleSet(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException {
158 return parseRuleSetNode(ruleSetReferenceId, ruleSetReferenceId.getInputStream(this.classLoader));
159 }
160
161 /**
162 * Create a Rule from a RuleSet created from a file name resource.
163 * The currently configured ClassLoader is used.
164 * <p>
165 * Any Rules in the RuleSet other than the one being created, are _not_ created.
166 *
167 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet with the Rule to create.
168 * @return A new Rule.
169 * @throws RuleSetNotFoundException if unable to find a resource.
170 */
171 private Rule createRule(RuleSetReferenceId ruleSetReferenceId) throws RuleSetNotFoundException {
172 if (ruleSetReferenceId.isAllRules()) {
173 throw new IllegalArgumentException("Cannot parse a single Rule from an all Rule RuleSet reference: <"
174 + ruleSetReferenceId + ">.");
175 }
176 RuleSet ruleSet = createRuleSet(ruleSetReferenceId);
177 return ruleSet.getRuleByName(ruleSetReferenceId.getRuleName());
178 }
179
180 /**
181 * Parse a ruleset node to construct a RuleSet.
182 *
183 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed.
184 * @param inputStream InputStream containing the RuleSet XML configuration.
185 * @return The new RuleSet.
186 */
187 private RuleSet parseRuleSetNode(RuleSetReferenceId ruleSetReferenceId, InputStream inputStream) {
188 if (!ruleSetReferenceId.isExternal()) {
189 throw new IllegalArgumentException("Cannot parse a RuleSet from a non-external reference: <"
190 + ruleSetReferenceId + ">.");
191 }
192 try {
193 DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
194 Document document = builder.parse(inputStream);
195 Element ruleSetElement = document.getDocumentElement();
196
197 RuleSet ruleSet = new RuleSet();
198 ruleSet.setFileName(ruleSetReferenceId.getRuleSetFileName());
199 ruleSet.setName(ruleSetElement.getAttribute("name"));
200
201 NodeList nodeList = ruleSetElement.getChildNodes();
202 for (int i = 0; i < nodeList.getLength(); i++) {
203 Node node = nodeList.item(i);
204 if (node.getNodeType() == Node.ELEMENT_NODE) {
205 if (node.getNodeName().equals("description")) {
206 ruleSet.setDescription(parseTextNode(node));
207 } else if (node.getNodeName().equals("include-pattern")) {
208 ruleSet.addIncludePattern(parseTextNode(node));
209 } else if (node.getNodeName().equals("exclude-pattern")) {
210 ruleSet.addExcludePattern(parseTextNode(node));
211 } else if (node.getNodeName().equals("rule")) {
212 parseRuleNode(ruleSetReferenceId, ruleSet, node);
213 } else {
214 throw new IllegalArgumentException("Unexpected element <" + node.getNodeName()
215 + "> encountered as child of <ruleset> element.");
216 }
217 }
218 }
219
220 return ruleSet;
221 } catch (ClassNotFoundException cnfe) {
222 return classNotFoundProblem(cnfe);
223 } catch (InstantiationException ie) {
224 return classNotFoundProblem(ie);
225 } catch (IllegalAccessException iae) {
226 return classNotFoundProblem(iae);
227 } catch (ParserConfigurationException pce) {
228 return classNotFoundProblem(pce);
229 } catch (RuleSetNotFoundException rsnfe) {
230 return classNotFoundProblem(rsnfe);
231 } catch (IOException ioe) {
232 return classNotFoundProblem(ioe);
233 } catch (SAXException se) {
234 return classNotFoundProblem(se);
235 }
236 }
237
238 private static RuleSet classNotFoundProblem(Exception ex) throws RuntimeException {
239 ex.printStackTrace();
240 throw new RuntimeException("Couldn't find the class " + ex.getMessage());
241 }
242
243 /**
244 * Parse a rule node.
245 *
246 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed.
247 * @param ruleSet The RuleSet being constructed.
248 * @param ruleNode Must be a rule element node.
249 */
250 private void parseRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode)
251 throws ClassNotFoundException, InstantiationException, IllegalAccessException, RuleSetNotFoundException {
252 Element ruleElement = (Element) ruleNode;
253 String ref = ruleElement.getAttribute("ref");
254 if (ref.endsWith("xml")) {
255 parseRuleSetReferenceNode(ruleSetReferenceId, ruleSet, ruleElement, ref);
256 } else if (StringUtil.isEmpty(ref)) {
257 parseSingleRuleNode(ruleSetReferenceId, ruleSet, ruleNode);
258 } else {
259 parseRuleReferenceNode(ruleSetReferenceId, ruleSet, ruleNode, ref);
260 }
261 }
262
263 /**
264 * Parse a rule node as an RuleSetReference for all Rules. Every Rule from
265 * the referred to RuleSet will be added as a RuleReference except for those
266 * explicitly excluded, below the minimum priority threshold for this
267 * RuleSetFactory, or which are deprecated.
268 *
269 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed.
270 * @param ruleSet The RuleSet being constructed.
271 * @param ruleElement Must be a rule element node.
272 * @param ref The RuleSet reference.
273 */
274 private void parseRuleSetReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Element ruleElement,
275 String ref) throws RuleSetNotFoundException {
276 RuleSetReference ruleSetReference = new RuleSetReference();
277 ruleSetReference.setAllRules(true);
278 ruleSetReference.setRuleSetFileName(ref);
279 NodeList excludeNodes = ruleElement.getChildNodes();
280 for (int i = 0; i < excludeNodes.getLength(); i++) {
281 if ((excludeNodes.item(i).getNodeType() == Node.ELEMENT_NODE)
282 && (excludeNodes.item(i).getNodeName().equals("exclude"))) {
283 Element excludeElement = (Element) excludeNodes.item(i);
284 ruleSetReference.addExclude(excludeElement.getAttribute("name"));
285 }
286 }
287
288 RuleSetFactory ruleSetFactory = new RuleSetFactory();
289 ruleSetFactory.setClassLoader(this.classLoader);
290 RuleSet otherRuleSet = ruleSetFactory.createRuleSet(RuleSetReferenceId.parse(ref).get(0));
291 for (Rule rule : otherRuleSet.getRules()) {
292 if (!ruleSetReference.getExcludes().contains(rule.getName())
293 && rule.getPriority().compareTo(minimumPriority) <= 0 && !rule.isDeprecated()) {
294 RuleReference ruleReference = new RuleReference();
295 ruleReference.setRuleSetReference(ruleSetReference);
296 ruleReference.setRule(rule);
297 ruleSet.addRule(ruleReference);
298 }
299 }
300 }
301
302 /**
303 * Parse a rule node as a single Rule. The Rule has been fully defined within
304 * the context of the current RuleSet.
305 *
306 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed.
307 * @param ruleSet The RuleSet being constructed.
308 * @param ruleNode Must be a rule element node.
309 */
310 private void parseSingleRuleNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode)
311 throws ClassNotFoundException, InstantiationException, IllegalAccessException {
312 Element ruleElement = (Element) ruleNode;
313
314 // Stop if we're looking for a particular Rule, and this element is not it.
315 if (!StringUtil.isEmpty(ruleSetReferenceId.getRuleName())
316 && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) {
317 return;
318 }
319
320 String attribute = ruleElement.getAttribute("class");
321 Class<?> c = classLoader.loadClass(attribute);
322 Rule rule = (Rule) c.newInstance();
323
324 rule.setName(ruleElement.getAttribute("name"));
325
326 if (ruleElement.hasAttribute("language")) {
327 String languageName = ruleElement.getAttribute("language");
328 Language language = Language.findByTerseName(languageName);
329 if (language == null) {
330 throw new IllegalArgumentException("Unknown Language '" + languageName + "' for Rule " + rule.getName()
331 + ", supported Languages are "
332 + Language.commaSeparatedTerseNames(Language.findWithRuleSupport()));
333 }
334 rule.setLanguage(language);
335 }
336
337 Language language = rule.getLanguage();
338 if (language == null) {
339 throw new IllegalArgumentException("Rule " + rule.getName()
340 + " does not have a Language; missing 'language' attribute?");
341 }
342
343 if (ruleElement.hasAttribute("minimumLanguageVersion")) {
344 String minimumLanguageVersionName = ruleElement.getAttribute("minimumLanguageVersion");
345 LanguageVersion minimumLanguageVersion = language.getVersion(minimumLanguageVersionName);
346 if (minimumLanguageVersion == null) {
347 throw new IllegalArgumentException("Unknown minimum Language Version '" + minimumLanguageVersionName
348 + "' for Language '" + language.getTerseName() + "' for Rule " + rule.getName()
349 + "; supported Language Versions are: "
350 + LanguageVersion.commaSeparatedTerseNames(language.getVersions()));
351 }
352 rule.setMinimumLanguageVersion(minimumLanguageVersion);
353 }
354
355 if (ruleElement.hasAttribute("maximumLanguageVersion")) {
356 String maximumLanguageVersionName = ruleElement.getAttribute("maximumLanguageVersion");
357 LanguageVersion maximumLanguageVersion = language.getVersion(maximumLanguageVersionName);
358 if (maximumLanguageVersion == null) {
359 throw new IllegalArgumentException("Unknown maximum Language Version '" + maximumLanguageVersionName
360 + "' for Language '" + language.getTerseName() + "' for Rule " + rule.getName()
361 + "; supported Language Versions are: "
362 + LanguageVersion.commaSeparatedTerseNames(language.getVersions()));
363 }
364 rule.setMaximumLanguageVersion(maximumLanguageVersion);
365 }
366
367 if (rule.getMinimumLanguageVersion() != null && rule.getMaximumLanguageVersion() != null) {
368 throw new IllegalArgumentException("The minimum Language Version '"
369 + rule.getMinimumLanguageVersion().getTerseName()
370 + "' must be prior to the maximum Language Version '"
371 + rule.getMaximumLanguageVersion().getTerseName() + "' for Rule " + rule.getName()
372 + "; perhaps swap them around?");
373 }
374
375 String since = ruleElement.getAttribute("since");
376 if (since.length() > 0) {
377 rule.setSince(since);
378 }
379 rule.setMessage(ruleElement.getAttribute("message"));
380 rule.setRuleSetName(ruleSet.getName());
381 rule.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));
382
383 if (ruleElement.hasAttribute("dfa") && ruleElement.getAttribute("dfa").equals("true")) {
384 rule.setUsesDFA();
385 }
386
387 if (ruleElement.hasAttribute("typeResolution") && ruleElement.getAttribute("typeResolution").equals("true")) {
388 rule.setUsesTypeResolution();
389 }
390
391 for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
392 Node node = ruleElement.getChildNodes().item(i);
393 if (node.getNodeType() == Node.ELEMENT_NODE) {
394 if (node.getNodeName().equals("description")) {
395 rule.setDescription(parseTextNode(node));
396 } else if (node.getNodeName().equals("example")) {
397 rule.addExample(parseTextNode(node));
398 } else if (node.getNodeName().equals("priority")) {
399 rule.setPriority(RulePriority.valueOf(Integer.parseInt(parseTextNode(node).trim())));
400 } else if (node.getNodeName().equals("properties")) {
401 parsePropertiesNode(rule, node);
402 } else {
403 throw new IllegalArgumentException("Unexpected element <" + node.getNodeName()
404 + "> encountered as child of <rule> element for Rule " + rule.getName());
405 }
406 }
407 }
408 if (!StringUtil.isEmpty(ruleSetReferenceId.getRuleName()) || rule.getPriority().compareTo(minimumPriority) <= 0) {
409 ruleSet.addRule(rule);
410 }
411 }
412
413 /**
414 * Parse a rule node as a RuleReference. A RuleReference is a single Rule
415 * which comes from another RuleSet with some of it's attributes potentially
416 * overridden.
417 *
418 * @param ruleSetReferenceId The RuleSetReferenceId of the RuleSet being parsed.
419 * @param ruleSet The RuleSet being constructed.
420 * @param ruleNode Must be a rule element node.
421 * @param ref A reference to a Rule.
422 */
423 private void parseRuleReferenceNode(RuleSetReferenceId ruleSetReferenceId, RuleSet ruleSet, Node ruleNode,
424 String ref) throws RuleSetNotFoundException {
425 Element ruleElement = (Element) ruleNode;
426
427 // Stop if we're looking for a particular Rule, and this element is not it.
428 if (!StringUtil.isEmpty(ruleSetReferenceId.getRuleName())
429 && !isRuleName(ruleElement, ruleSetReferenceId.getRuleName())) {
430 return;
431 }
432
433 RuleSetFactory ruleSetFactory = new RuleSetFactory();
434 ruleSetFactory.setClassLoader(this.classLoader);
435
436 RuleSetReferenceId otherRuleSetReferenceId = RuleSetReferenceId.parse(ref).get(0);
437 if (!otherRuleSetReferenceId.isExternal()) {
438 otherRuleSetReferenceId = new RuleSetReferenceId(ref, ruleSetReferenceId);
439 }
440 Rule referencedRule = ruleSetFactory.createRule(otherRuleSetReferenceId);
441 if (referencedRule == null) {
442 throw new IllegalArgumentException("Unable to find referenced rule "
443 + otherRuleSetReferenceId.getRuleName() + "; perhaps the rule name is mispelled?");
444 }
445
446 if (warnDeprecated && referencedRule.isDeprecated()) {
447 if (referencedRule instanceof RuleReference) {
448 RuleReference ruleReference = (RuleReference) referencedRule;
449 LOG.warning("Use Rule name " + ruleReference.getRuleSetReference().getRuleSetFileName() + "/"
450 + ruleReference.getName() + " instead of the deprecated Rule name " + otherRuleSetReferenceId
451 + ". Future versions of PMD will remove support for this deprecated Rule name usage.");
452 } else if (referencedRule instanceof MockRule) {
453 LOG.warning("Discontinue using Rule name " + otherRuleSetReferenceId
454 + " as it has been removed from PMD and no longer functions."
455 + " Future versions of PMD will remove support for this Rule.");
456 } else {
457 LOG.warning("Discontinue using Rule name " + otherRuleSetReferenceId
458 + " as it is scheduled for removal from PMD."
459 + " Future versions of PMD will remove support for this Rule.");
460 }
461 }
462
463 RuleSetReference ruleSetReference = new RuleSetReference();
464 ruleSetReference.setAllRules(false);
465 ruleSetReference.setRuleSetFileName(otherRuleSetReferenceId.getRuleSetFileName());
466
467 RuleReference ruleReference = new RuleReference();
468 ruleReference.setRuleSetReference(ruleSetReference);
469 ruleReference.setRule(referencedRule);
470
471 if (ruleElement.hasAttribute("deprecated")) {
472 ruleReference.setDeprecated(Boolean.parseBoolean(ruleElement.getAttribute("deprecated")));
473 }
474 if (ruleElement.hasAttribute("name")) {
475 ruleReference.setName(ruleElement.getAttribute("name"));
476 }
477 if (ruleElement.hasAttribute("message")) {
478 ruleReference.setMessage(ruleElement.getAttribute("message"));
479 }
480 if (ruleElement.hasAttribute("externalInfoUrl")) {
481 ruleReference.setExternalInfoUrl(ruleElement.getAttribute("externalInfoUrl"));
482 }
483 for (int i = 0; i < ruleElement.getChildNodes().getLength(); i++) {
484 Node node = ruleElement.getChildNodes().item(i);
485 if (node.getNodeType() == Node.ELEMENT_NODE) {
486 if (node.getNodeName().equals("description")) {
487 ruleReference.setDescription(parseTextNode(node));
488 } else if (node.getNodeName().equals("example")) {
489 ruleReference.addExample(parseTextNode(node));
490 } else if (node.getNodeName().equals("priority")) {
491 ruleReference.setPriority(RulePriority.valueOf(Integer.parseInt(parseTextNode(node))));
492 } else if (node.getNodeName().equals("properties")) {
493 parsePropertiesNode(ruleReference, node);
494 } else {
495 throw new IllegalArgumentException("Unexpected element <" + node.getNodeName()
496 + "> encountered as child of <rule> element for Rule " + ruleReference.getName());
497 }
498 }
499 }
500
501 if (!StringUtil.isEmpty(ruleSetReferenceId.getRuleName())
502 || referencedRule.getPriority().compareTo(minimumPriority) <= 0) {
503 ruleSet.addRule(ruleReference);
504 }
505 }
506
507 /**
508 * Parse a properties node.
509 *
510 * @param rule The Rule to which the properties should be added.
511 * @param propertiesNode Must be a properties element node.
512 */
513 private static void parsePropertiesNode(Rule rule, Node propertiesNode) {
514 for (int i = 0; i < propertiesNode.getChildNodes().getLength(); i++) {
515 Node node = propertiesNode.getChildNodes().item(i);
516 if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("property")) {
517 parsePropertyNode(rule, node);
518 }
519 }
520 }
521
522 /**
523 * Parse a property node.
524 *
525 * @param rule The Rule to which the property should be added.
526 * @param propertyNode Must be a property element node.
527 */
528 @SuppressWarnings("unchecked")
529 private static void parsePropertyNode(Rule rule, Node propertyNode) {
530 Element propertyElement = (Element) propertyNode;
531 String name = propertyElement.getAttribute("name");
532 String description = propertyElement.getAttribute("description");
533 String type = propertyElement.getAttribute("type");
534 String delimiter = propertyElement.getAttribute("delimiter");
535 String min = propertyElement.getAttribute("min");
536 String max = propertyElement.getAttribute("max");
537 String value = propertyElement.getAttribute("value");
538
539 // If value not provided, get from child <value> element.
540 if (StringUtil.isEmpty(value)) {
541 for (int i = 0; i < propertyNode.getChildNodes().getLength(); i++) {
542 Node node = propertyNode.getChildNodes().item(i);
543 if ((node.getNodeType() == Node.ELEMENT_NODE) && node.getNodeName().equals("value")) {
544 value = parseTextNode(node);
545 }
546 }
547 }
548
549 // Setting of existing property, or defining a new property?
550 if (StringUtil.isEmpty(type)) {
551 PropertyDescriptor propertyDescriptor = rule.getPropertyDescriptor(name);
552 if (propertyDescriptor == null) {
553 throw new IllegalArgumentException("Cannot set non-existant property '" + name + "' on Rule "
554 + rule.getName());
555 } else {
556 Object realValue = propertyDescriptor.valueFrom(value);
557 rule.setProperty(propertyDescriptor, realValue);
558 }
559 } else {
560 PropertyDescriptor propertyDescriptor = PropertyDescriptorFactory.createPropertyDescriptor(name,
561 description, type, delimiter, min, max, value);
562 rule.definePropertyDescriptor(propertyDescriptor);
563 }
564 }
565
566 /**
567 * Parse a String from a textually type node.
568 *
569 * @param node The node.
570 * @return The String.
571 */
572 private static String parseTextNode(Node node) {
573
574 final int nodeCount = node.getChildNodes().getLength();
575 if (nodeCount == 0) {
576 return "";
577 }
578
579 StringBuilder buffer = new StringBuilder();
580
581 for (int i = 0; i < nodeCount; i++) {
582 Node childNode = node.getChildNodes().item(i);
583 if (childNode.getNodeType() == Node.CDATA_SECTION_NODE || childNode.getNodeType() == Node.TEXT_NODE) {
584 buffer.append(childNode.getNodeValue());
585 }
586 }
587 return buffer.toString();
588 }
589
590 /**
591 * Determine if the specified rule element will represent a Rule with the given name.
592 * @param ruleElement The rule element.
593 * @param ruleName The Rule name.
594 * @return <code>true</code> if the Rule would have the given name, <code>false<code> otherwise.
595 */
596 private boolean isRuleName(Element ruleElement, String ruleName) {
597 if (ruleElement.hasAttribute("name")) {
598 return ruleElement.getAttribute("name").equals(ruleName);
599 } else if (ruleElement.hasAttribute("ref")) {
600 RuleSetReferenceId ruleSetReferenceId = RuleSetReferenceId.parse(ruleElement.getAttribute("ref")).get(0);
601 return ruleSetReferenceId.getRuleName() != null && ruleSetReferenceId.getRuleName().equals(ruleName);
602 } else {
603 return false;
604 }
605 }
606 }
|