001: package org.acm.seguin.pmd;
002:
003: import org.acm.seguin.pmd.swingui.event.ListenerList;
004: import org.acm.seguin.pmd.swingui.event.PMDDirectoryRequestEvent;
005: import org.acm.seguin.pmd.swingui.event.PMDDirectoryRequestEventListener;
006: import org.acm.seguin.pmd.swingui.event.PMDDirectoryReturnedEvent;
007: import org.acm.seguin.pmd.swingui.event.RuleSetEvent;
008: import org.acm.seguin.pmd.swingui.event.RuleSetEventListener;
009:
010: import java.io.File;
011: import java.io.FileInputStream;
012: import java.io.FileNotFoundException;
013: import java.io.FileOutputStream;
014: import java.io.FilenameFilter;
015: import java.io.IOException;
016: import java.text.MessageFormat;
017: import java.util.ArrayList;
018: import java.util.Iterator;
019: import java.util.List;
020: import java.util.Properties;
021:
022: /**
023: * Defines and provides access to PMD's directory structure. The user defines the location
024: * of the root PMD directory, e.g., /users/userA/PMD. The PMD directory structure provides
025: * the following:
026: * <ul>
027: * <li>Organization to simplify PMD's access to files.</li>
028: * <li>Eliminates dependence of manually updating the Java classpath.</li>
029: * <li>Permits adding and removing rule sets without updating lists.</li>
030: * </ul>
031: * <pre>
032: * The directory structure and contents are the following:
033: * <code>
034: * PMD
035: * pmd.properties
036: * rulesets
037: * basic.xml
038: * design.xml
039: * import.xml
040: * com
041: * myCompany
042: * pmd
043: * rules
044: * myRule01.class
045: * myRule02.class
046: * myRule03.class
047: * net
048: * sourceforge
049: * pmd
050: * rules
051: * myNewExperimentalRule.class
052: * </code>
053: * </pre>
054: * The <b>PMD</b> directory is the root directory of all PMD files.
055: * <p>
056: * The <b>pmd.properties</b> file contains various information to be defined.
057: * <p>
058: * The <b>rulesets</b> directory contains the rule set files and rule class file directories.
059: * <p>
060: * A <b>rule set file</b> is a XML file that describes the rule set and its rules. This
061: * information is displayed and maintained in the PMD Viewer. The rule class files are called by PMD
062: * to perform the analysis.
063: * <p>
064: * All rule classes, other than the rule classes in pmd.jar, are stored in directory paths
065: * defined by each rule's class name. The Java classpath is appended with the rulesets
066: * directory so that the rule class and any supporting class files may be found.
067: * <p>
068: * <b>NOTE:</b> The user's home directory will contain a PMD directory with a user.preferences
069: * file. An entry in the user's preferences will be the path to the PMD root directory
070: * described above.
071: *
072: * @author Donald A. Leckie
073: * @since September 19, 2002
074: * @version $Revision: 1.1 $, $Date: 2003/07/29 20:51:58 $
075: */
076:
077: public class PMDDirectory {
078:
079: private String m_pmdDirectoryPath;
080: private String m_ruleSetsDirectoryPath;
081: private Properties m_properties;
082: private PMDDirectoryRequestEventHandler m_pmdDirectoryRequestEventHandler;
083: private RuleSetEventHandler m_ruleSetEventHandler;
084: private static PMDDirectory m_pmdDirectoryInstance;
085:
086: // Constants
087: private final String PROPERTIES_FILE_NAME = "pmd.properties";
088:
089: /**
090: ********************************************************************************
091: *
092: * Creates the information about the PMD directory structure that will be required
093: * for accessing the PMD files.
094: *
095: * @param pathToPMD The full path to the PMD directory, but excludes the PMD directory.
096: */
097: private PMDDirectory(String pathToPMD) throws PMDException {
098: String classpath;
099: String key;
100:
101: m_pmdDirectoryRequestEventHandler = new PMDDirectoryRequestEventHandler();
102: m_ruleSetEventHandler = new RuleSetEventHandler();
103: ListenerList.addListener(m_pmdDirectoryRequestEventHandler);
104: ListenerList.addListener(m_ruleSetEventHandler);
105: m_pmdDirectoryPath = pathToPMD + File.separator + "PMD";
106: m_ruleSetsDirectoryPath = m_pmdDirectoryPath + File.separator
107: + "rulesets";
108: key = "java.class.path";
109: classpath = System.getProperty(key);
110: classpath = classpath + ";" + m_ruleSetsDirectoryPath;
111: System.setProperty(key, classpath);
112: loadPropertiesFile();
113: }
114:
115: /**
116: ********************************************************************************
117: *
118: * @param pathToPMD The full path to the PMD directory, but excludes the PMD directory.
119: */
120: public static final void open(String pathToPMD) throws PMDException {
121: m_pmdDirectoryInstance = new PMDDirectory(pathToPMD);
122: }
123:
124: /**
125: ********************************************************************************
126: *
127: * @return
128: */
129: public static final PMDDirectory getDirectory() {
130: return m_pmdDirectoryInstance;
131: }
132:
133: /**
134: ********************************************************************************
135: *
136: * Gets a rule set containing only the rule sets and rules to be included for running
137: * the analysis.
138: *
139: * @return A rule containing only included rules.
140: */
141: public RuleSet getIncludedRules(int lowestPriorityForAnalysis)
142: throws PMDException {
143: RuleSet includedRules = new RuleSet();
144: Iterator ruleSetFiles = getRuleSetFiles().iterator();
145:
146: while (ruleSetFiles.hasNext()) {
147: File ruleSetFile = (File) ruleSetFiles.next();
148: RuleSet ruleSet = getRuleSet(ruleSetFile, true);
149:
150: if ((ruleSet != null) && ruleSet.include()) {
151: Iterator allRules = ruleSet.getRules().iterator();
152:
153: while (allRules.hasNext()) {
154: Rule rule = (Rule) allRules.next();
155:
156: if (rule.include()) {
157: if (rule.getPriority() <= lowestPriorityForAnalysis) {
158: includedRules.addRule(rule);
159: }
160: }
161: }
162: }
163: }
164:
165: return includedRules;
166: }
167:
168: /**
169: ********************************************************************************
170: *
171: * Gets the rule set for the given rule set file. All rules in the rule set file
172: * are stored in the rule set regardless of their <i>include</i> state.
173: *
174: * @param ruleSetFile The file of the desired rule set.
175: *
176: * @return A rule set containing all of its rules.
177: *
178: * @throws PMDException
179: */
180: public RuleSet getRuleSet(File ruleSetFile) throws PMDException {
181: return getRuleSet(ruleSetFile, false);
182: }
183:
184: /**
185: ********************************************************************************
186: *
187: * Gets the rule set for the given rule set File. All rules in the rule set file
188: * are stored in the rule set according of their <i>include</i> state and the <i>onlyIfIncluded</i>
189: * flag.
190: *
191: * @param ruleSetFile The file of the desired rule set.
192: *
193: * @return A rule set containing all of its rules.
194: *
195: * @throws PMDException
196: */
197: public RuleSet getRuleSet(File ruleSetFile, boolean onlyIfIncluded)
198: throws PMDException {
199: if (ruleSetFile == null) {
200: String message = "Rule set file parameter is missing.";
201: PMDException exception = new PMDException(message);
202: exception.fillInStackTrace();
203: throw exception;
204: }
205:
206: FileInputStream inputStream = null;
207: RuleSet ruleSet = null;
208:
209: try {
210: RuleSetReader reader;
211: inputStream = new FileInputStream(ruleSetFile);
212: reader = new RuleSetReader();
213: ruleSet = reader.read(inputStream, ruleSetFile.getName(),
214: onlyIfIncluded);
215: } catch (FileNotFoundException exception) {
216: String template = "Rule set \"{0}\" was not found.";
217: String[] args = { ruleSetFile.getPath() };
218: String message = MessageFormat.format(template, args);
219: PMDException pmdException = new PMDException(message,
220: exception);
221: pmdException.fillInStackTrace();
222: throw pmdException;
223: } finally {
224: if (inputStream != null) {
225: try {
226: inputStream.close();
227: } catch (IOException exception) {
228: }
229: }
230: }
231:
232: return ruleSet;
233: }
234:
235: /**
236: ********************************************************************************
237: *
238: * @return
239: */
240: private List getRuleSetFiles() {
241: List ruleSetFiles = new ArrayList();
242: File directory = new File(m_ruleSetsDirectoryPath);
243:
244: if (directory.exists() == false) {
245: directory.mkdirs();
246: }
247:
248: File[] files = directory.listFiles(new XMLFileNameFilter());
249:
250: for (int n = 0; n < files.length; n++) {
251: ruleSetFiles.add(files[n]);
252: }
253:
254: return ruleSetFiles;
255: }
256:
257: /**
258: ********************************************************************************
259: *
260: * @return
261: */
262: public List getRegisteredRuleSets() {
263: List ruleSetList = new ArrayList();
264:
265: try {
266: Iterator ruleSets = (new RuleSetFactory())
267: .getRegisteredRuleSets();
268:
269: while (ruleSets.hasNext()) {
270: RuleSet ruleSet;
271: Iterator rules;
272:
273: ruleSet = (RuleSet) ruleSets.next();
274: ruleSet.setInclude(true);
275: rules = ruleSet.getRules().iterator();
276:
277: while (rules.hasNext()) {
278: ((Rule) rules.next()).setInclude(true);
279: }
280:
281: ruleSetList.add(ruleSet);
282: }
283: } catch (RuleSetNotFoundException exception) {
284: // This should not happen because the registered rule sets are resources in pmd.jar.
285: System.out.println(exception.getMessage());
286: }
287:
288: return ruleSetList;
289: }
290:
291: /**
292: ********************************************************************************
293: *
294: * @return
295: */
296: public List getRuleSets() throws PMDException {
297: List ruleSetList;
298: List ruleSetFilesList = getRuleSetFiles();
299:
300: if (ruleSetFilesList.size() == 0) {
301: ruleSetList = getRegisteredRuleSets();
302: } else {
303: Iterator ruleSetFiles;
304:
305: ruleSetList = new ArrayList();
306: ruleSetFiles = ruleSetFilesList.iterator();
307:
308: while (ruleSetFiles.hasNext()) {
309: File ruleSetFile = (File) ruleSetFiles.next();
310: RuleSet ruleSet = getRuleSet(ruleSetFile);
311:
312: ruleSetList.add(ruleSet);
313: }
314: }
315:
316: return ruleSetList;
317: }
318:
319: /**
320: ********************************************************************************
321: *
322: * @return
323: */
324: public String getPMDDirectoryPath() {
325: return m_pmdDirectoryPath;
326: }
327:
328: /**
329: ********************************************************************************
330: *
331: * @return
332: */
333: public String getRuleSetsDirectoryPath() {
334: return m_ruleSetsDirectoryPath;
335: }
336:
337: /**
338: ********************************************************************************
339: *
340: * @param ruleSetList
341: */
342: protected void saveRuleSets(List ruleSetList) {
343: Iterator ruleSets = ruleSetList.iterator();
344:
345: while (ruleSets.hasNext()) {
346: RuleSet ruleSet = (RuleSet) ruleSets.next();
347: String ruleSetFileName = ruleSet.getFileName();
348: String path = m_ruleSetsDirectoryPath + File.separator
349: + ruleSetFileName;
350: File file = new File(path);
351: FileOutputStream outputStream = null;
352:
353: if (file.exists()) {
354: file.delete();
355: }
356:
357: try {
358: RuleSetWriter writer;
359:
360: outputStream = new FileOutputStream(file);
361: writer = new RuleSetWriter(outputStream);
362: writer.write(ruleSet);
363: } catch (FileNotFoundException exception) {
364: // Should not reach here because the rule set file has been deleted if it
365: // existed and the directories all exist.
366: exception = null;
367: } finally {
368: if (outputStream != null) {
369: try {
370: outputStream.close();
371: } catch (IOException exception) {
372: exception = null;
373: }
374: }
375: }
376: }
377: }
378:
379: /**
380: ********************************************************************************
381: *
382: * @param pathToPMD
383: */
384: private void loadPropertiesFile() throws PMDException {
385: String propertiesFileName;
386: FileInputStream inputStream;
387:
388: propertiesFileName = m_pmdDirectoryPath + File.separator
389: + PROPERTIES_FILE_NAME;
390: m_properties = new Properties();
391: inputStream = null;
392:
393: try {
394: File file = new File(propertiesFileName);
395:
396: if (file.exists() == false) {
397: File directory = file.getParentFile();
398:
399: directory.mkdirs();
400: file.createNewFile();
401: }
402:
403: inputStream = new FileInputStream(propertiesFileName);
404: m_properties.load(inputStream);
405: } catch (FileNotFoundException exception) {
406: String template = "Could not find the file \"{0}\".";
407: String[] args = { propertiesFileName };
408: String message = MessageFormat.format(template, args);
409: PMDException pmdException = new PMDException(message,
410: exception);
411: pmdException.fillInStackTrace();
412: throw pmdException;
413: } catch (IOException exception) {
414: String template = "Unable to read the file \"{0}\".";
415: String[] args = { propertiesFileName };
416: String message = MessageFormat.format(template, args);
417: PMDException pmdException = new PMDException(message,
418: exception);
419: pmdException.fillInStackTrace();
420: throw pmdException;
421: } finally {
422: if (inputStream != null) {
423: try {
424: inputStream.close();
425: } catch (IOException exception) {
426: exception = null;
427: }
428: }
429: }
430: }
431:
432: /**
433: ********************************************************************************
434: *
435: */
436: public void savePropertiesFile() throws PMDException {
437: FileOutputStream outputStream = null;
438: String propertiesFileName = m_pmdDirectoryPath + File.separator
439: + PROPERTIES_FILE_NAME;
440: File file = new File(propertiesFileName);
441: if (file.exists()) {
442: file.delete();
443: }
444:
445: try {
446: m_properties.store(outputStream, null);
447: } catch (FileNotFoundException exception) {
448: String template = "Could not find the file \"{0}\".";
449: String[] args = { propertiesFileName };
450: String message = MessageFormat.format(template, args);
451: PMDException pmdException = new PMDException(message,
452: exception);
453: pmdException.fillInStackTrace();
454: throw pmdException;
455: } catch (IOException exception) {
456: String template = "Unable to read the file \"{0}\".";
457: String[] args = { propertiesFileName };
458: String message = MessageFormat.format(template, args);
459: PMDException pmdException = new PMDException(message,
460: exception);
461: pmdException.fillInStackTrace();
462: throw pmdException;
463: } finally {
464: if (outputStream != null) {
465: try {
466: outputStream.close();
467: } catch (IOException exception) {
468: exception = null;
469: }
470: }
471: }
472: }
473:
474: /**
475: *******************************************************************************
476: *******************************************************************************
477: *******************************************************************************
478: */
479: private class RuleSetEventHandler implements RuleSetEventListener {
480:
481: /**
482: ***************************************************************************
483: *
484: * @param event
485: */
486: public void saveRuleSets(RuleSetEvent event) {
487: List ruleSetList = event.getRuleSetList();
488: PMDDirectory.this .saveRuleSets(ruleSetList);
489: }
490: }
491:
492: /**
493: *******************************************************************************
494: *******************************************************************************
495: *******************************************************************************
496: */
497: private class PMDDirectoryRequestEventHandler implements
498: PMDDirectoryRequestEventListener {
499:
500: /**
501: ***************************************************************************
502: *
503: * @param event
504: */
505: public void requestRuleSetPath(PMDDirectoryRequestEvent event) {
506: PMDDirectoryReturnedEvent.notifyReturnedRuleSetPath(this ,
507: getRuleSetsDirectoryPath());
508: }
509:
510: /**
511: ***************************************************************************
512: *
513: * @param event
514: */
515: public void requestAllRuleSets(PMDDirectoryRequestEvent event)
516: throws PMDException {
517: PMDDirectoryReturnedEvent.notifyReturnedAllRuleSets(this ,
518: getRuleSets());
519: }
520:
521: /**
522: ***************************************************************************
523: *
524: * @param event
525: */
526: public void requestDefaultRuleSets(
527: PMDDirectoryRequestEvent event) {
528: PMDDirectoryReturnedEvent.notifyReturnedDefaultRuleSets(
529: this , getRegisteredRuleSets());
530: }
531:
532: /**
533: ***************************************************************************
534: *
535: * @param event
536: */
537: public void requestIncludedRules(PMDDirectoryRequestEvent event)
538: throws PMDException {
539: int priority = event.getLowestPriorityForAnalysis();
540: PMDDirectoryReturnedEvent.notifyReturnedIncludedRules(this ,
541: getIncludedRules(priority));
542: }
543: }
544:
545: /**
546: *******************************************************************************
547: *******************************************************************************
548: *******************************************************************************
549: */
550: private class XMLFileNameFilter implements FilenameFilter {
551:
552: /**
553: ***************************************************************************
554: *
555: * @param directory
556: * @param fileName
557: *
558: * @return
559: */
560: public boolean accept(File directory, String fileName) {
561: return fileName.toLowerCase().endsWith(".xml");
562: }
563: }
564: }
|