001: package csdl.jblanket.app;
002:
003: import csdl.jblanket.JBlanketException;
004: import csdl.jblanket.app.tree.NodeContentHandler;
005: import csdl.jblanket.app.tree.NodeErrorHandler;
006: import csdl.jblanket.app.tree.MethodNode;
007: import csdl.jblanket.app.tree.Node;
008: import csdl.jblanket.app.tree.TreeWriter;
009: import csdl.jblanket.util.MethodCategories;
010:
011: import java.awt.BorderLayout;
012: import java.awt.Color;
013: import java.awt.Container;
014: import java.awt.Dimension;
015: import java.awt.FlowLayout;
016: import java.awt.GridBagConstraints;
017: import java.awt.GridBagLayout;
018: import java.awt.GridLayout;
019: import java.awt.event.ActionEvent;
020: import java.awt.event.ActionListener;
021: import java.awt.event.WindowAdapter;
022: import java.awt.event.WindowEvent;
023: import java.io.File;
024: import java.io.FileInputStream;
025: import java.io.FileOutputStream;
026: import java.io.IOException;
027: import java.util.ArrayList;
028: import java.util.Enumeration;
029: import java.util.Iterator;
030: import java.util.List;
031:
032: import javax.swing.JButton;
033: import javax.swing.JOptionPane;
034: import javax.swing.JPanel;
035: import javax.swing.JScrollPane;
036: import javax.swing.JFrame;
037: import javax.swing.JTextPane;
038: import javax.swing.JTree;
039: import javax.swing.UIManager;
040: import javax.swing.text.BadLocationException;
041: import javax.swing.text.Document;
042: import javax.swing.text.Style;
043: import javax.swing.text.StyleConstants;
044: import javax.swing.text.StyleContext;
045: import javax.swing.tree.DefaultMutableTreeNode;
046: import javax.swing.tree.DefaultTreeModel;
047: import javax.swing.tree.TreePath;
048: import javax.swing.tree.TreeSelectionModel; //import javax.xml.parsers.SAXParserFactory;
049:
050: import org.apache.xerces.parsers.SAXParser;
051: import org.xml.sax.InputSource;
052: import org.xml.sax.XMLReader;
053: import org.xml.sax.SAXException;
054:
055: /**
056: * Provides the application used to exclude individual methods from the coverage measurement. It
057: * displays a mapping of a system's package structure to facilitate selection of individual methods
058: * and deciding what to do with them.
059: * <p>
060: * This is an optional step in JBlanket that adds to the power of Extreme Coverage.
061: * <p>
062: * The above mentioned funcitonality is achieved by invoking the <code>main</code> method.
063: * Arguments to pass to the main method include:
064: * <p>
065: * <b>Required</b> command line arguments:
066: * <ul>
067: * <p>
068: * 'total.testedFile' - file containing all the tested methods before any optional exclusions,
069: * e.g., one-line methods, etc., are removed<br>
070: * <i>For example</i>: -total.testedFile
071: * <p>
072: * 'total.untestedFile' - file containing all the untested methods before any optional exclusions,
073: * e.g., one-line methods, etc., are removed<br>
074: * <i>For example</i>: -total.untestedFile
075: * </ul>
076: * <p>
077: * <b>Optional</b> command line arguments:
078: * <ul>
079: * <p>
080: * 'verbose' - describes if application should execute in verbose mode<br>
081: * <i>For example</i>: -verbose true
082: * <p>
083: * 'excludeOneLineMethods' - describes if one-line methods should be distinguished<br>
084: * <i>For example</i>: -excludeOneLineMethods false
085: * <p>
086: * 'excludeConstructors' - describes if constructors should be distinguished<br>
087: * <i>For example</i>: -excludeConstructors false
088: * <p>
089: * <p>
090: * 'onelineFile' - name of the file containing all one-line methods<br>
091: * <i>For example</i>: -onelineFile oneLineMethods.xml
092: * <p>
093: * 'constructorFile' - name of the file containing all constructors<br>
094: * <i>For example</i>: -constructorFile constructorMethods.xml
095: * <p>
096: * 'excludedIndividualFile' - name of the file containing all indiviually excluded methods</br>
097: * <i>For example</i>: -excludedIndividualFile excludedIndividualMethods.xml
098: * </ul>
099: * <p>
100: * An example of the most basic command line is:<br>
101: * <pre>
102: * jblanket>java -D"jblanket.dir"=.\build\jblanket
103: * -classpath .;.\lib\ant\jblanket.jar;.\lib\ant\jdom.jar;.\lib\ant\xerces.jar
104: * csdl.jblanket.app.ExcludeIndividualMethodApp
105: * -total.testedFile -total.untestedFile
106: * </pre>
107: * <p>
108: * Another one that excludes one-line methods in a file myOneLineMethods.xml is:<br>
109: * <pre>
110: * jblanket>java -D"jblanket.dir"=.\build\jblanket
111: * -classpath .;.\lib\ant\jblanket.jar;.\lib\ant\jdom.jar;.\lib\ant\xerces.jar
112: * csdl.jblanket.app.ExcludeIndividualMethodApp
113: * -total.testedFile -total.untestedFile -excludeOneLineMethods true
114: * -oneLineFile myOneLineMethods.xml
115: * </pre>
116: *
117: * @author Joy M. Agustin
118: * @version $Id: ExcludeIndividualMethodApp.java,v 1.1 2004/11/07 00:32:41 timshadel Exp $
119: */
120: public class ExcludeIndividualMethodApp extends JPanel {
121:
122: /** Optionally play with line styles. Possible values are
123: "Angled", "Horizontal", and "None" (the default). */
124: private boolean playWithLineStyle = false;
125: private String lineStyle = "Angled";
126:
127: /** Tree holding all data */
128: private DefaultTreeModel treeModel;
129: /** Tree displayed in application */
130: private final JTree tree;
131: /** Frame containing this application */
132: private JFrame frame;
133:
134: /** Method categories to use */
135: private MethodCategories categories;
136: /** Describes if one-line methods should be excluded from the coverage measurement */
137: protected boolean excludeOneLineMethods;
138: /** Describes if constructors should be excluded from the coverage measurement */
139: protected boolean excludeConstructors;
140:
141: /**
142: * Creates a new ExcludeIndividualMethodApp.
143: *
144: * @param verbose describes if JBlanket should execute in verbose mode.
145: * @param excludeOneLineMethods describes if one-line methods should be excluded.
146: * @param excludeConstructors describes if constructors should be excluded.
147: * @param frame the frame to place this JPanel.
148: * @throws JBlanketException if unable to create a tree.
149: */
150: public ExcludeIndividualMethodApp(boolean verbose,
151: boolean excludeOneLineMethods, boolean excludeConstructors,
152: JFrame frame) throws JBlanketException {
153:
154: this .categories = MethodCategories.getInstance();
155:
156: this .excludeOneLineMethods = excludeOneLineMethods;
157: this .excludeConstructors = excludeConstructors;
158:
159: //Create the nodes and tree.
160: DefaultMutableTreeNode top = new DefaultMutableTreeNode(
161: new Node("System"));
162: treeModel = new DefaultTreeModel(top);
163: createTree();
164:
165: //Create a tree that allows multiple selections at a time.
166: this .tree = new JTree(treeModel);
167: this .tree.getSelectionModel().setSelectionMode(
168: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
169: this .tree.setCellRenderer(new AppRenderer());
170:
171: setLayout(new BorderLayout());
172: setPreferredSize(new Dimension(600, 500));
173:
174: //Create the scroll pane and add the tree to it.
175: JScrollPane treeView = new JScrollPane(tree);
176: add(treeView, BorderLayout.CENTER);
177:
178: JPanel bottomPanel = new JPanel(new GridLayout(0, 1));
179: bottomPanel.add(createButtonPanel());
180: add(bottomPanel, BorderLayout.EAST);
181:
182: this .frame = frame;
183: }
184:
185: /**
186: * Creates the tree structure from the files stored in <code>categories</code>.
187: *
188: * @throws JBlanketException if unable to parse an XML file.
189: */
190: private void createTree() throws JBlanketException {
191: // check if corresponding files were added
192: if (this .excludeOneLineMethods) {
193: this .categories.getFileName("oneLineFile");
194: }
195: if (this .excludeConstructors) {
196: this .categories.getFileName("constructorFile");
197: }
198: // automatically excluded
199: this .categories.getFileName("excludedIndividualFile");
200:
201: Iterator i = this .categories.getCategories().iterator();
202: while (i.hasNext()) {
203: String key = (String) i.next();
204: File nextFile = new File((String) this .categories
205: .getFileName(key));
206: if (!(key.equals("excludedIndividualFile") && !nextFile
207: .exists())) {
208: parseXmlFile(nextFile, key);
209: }
210: }
211:
212: }
213:
214: /**
215: * Parses <code>fileName</code> and aggregates its contained methods into a common repository.
216: *
217: * @param file the XML file to parse.
218: * @param methodCategory the category of methods in <code>fileName</code>.
219: * @throws JBlanketException if unable to open, read, or parse <code>fileName</code>.
220: */
221: private void parseXmlFile(File file, String methodCategory)
222: throws JBlanketException {
223: // create instances needed for parsing
224: XMLReader reader = new SAXParser();
225: /* Attempt at replacing Xerces with Java 1.4 Standard Library
226: XMLReader reader = null;
227: try {
228: // throws SAXException, ParserConfigurationException
229: reader = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
230: }
231: catch (Exception e) {
232: throw new JBlanketException("", e);
233: }
234: */
235: // register content handler
236: reader.setContentHandler(new NodeContentHandler(this .treeModel,
237: methodCategory));
238: // register error handler
239: reader.setErrorHandler(new NodeErrorHandler());
240: // parse
241: InputSource inputSource = null;
242:
243: try {
244: inputSource = new InputSource(new FileInputStream(file));
245: } catch (IOException e) {
246: throw new JBlanketException("Unable to open file " + file);
247: }
248:
249: inputSource.setSystemId(file.getAbsolutePath());
250:
251: try {
252: reader.parse(inputSource);
253: } catch (SAXException e) {
254: throw new JBlanketException("Unable to parse file " + file,
255: e);
256: } catch (IOException e) {
257: throw new JBlanketException("Unable to read file " + file,
258: e);
259: }
260: }
261:
262: /**
263: * Creates the legend at the top of the application
264: *
265: * @return filled out JPanel, ready to add to a component.
266: */
267: private JPanel createLegend() {
268:
269: // crate list for legend
270: List legendList = new ArrayList();
271: legendList.add("plain -- tested\n");
272: legendList.add("BOLD");
273: legendList.add(" -- untested\n");
274: legendList.add("black -- included\n");
275:
276: // if (this.excludeConstructors) {
277: legendList.add("blue");
278: legendList.add(" -- constructor\n");
279: // }
280:
281: // if (this.excludeOneLineMethods) {
282: legendList.add("purple");
283: legendList.add(" -- one-line\n");
284: // }
285:
286: legendList.add("red");
287: legendList.add(" -- user excluded\n");
288:
289: // create list of styles
290: List stylesList = new ArrayList();
291: stylesList.add("regular");
292: stylesList.add("bold");
293: stylesList.add("regular");
294: stylesList.add("black");
295:
296: // if (this.excludeConstructors) {
297: stylesList.add("blue");
298: stylesList.add("regular");
299: // }
300:
301: // if (this.excludeOneLineMethods) {
302: stylesList.add("purple");
303: stylesList.add("regular");
304: // }
305: stylesList.add("red");
306: stylesList.add("regular");
307:
308: JTextPane legendPane = new JTextPane();
309: legendPane.setEditable(false);
310: //background color of com.sun.java.swing.plaf.motif.MotifLookAndFeel
311: legendPane.setBackground(new Color(175, 180, 199));
312: //background color of com.sun.java.swing.plaf.windows.WindowsLookAndFeel
313: // legendPane.setBackground(new Color(209, 205, 197));
314:
315: initStylesForTextPane(legendPane);
316: Document doc = legendPane.getDocument();
317: try {
318:
319: String[] legend = (String[]) legendList
320: .toArray(new String[legendList.size()]);
321: String[] styles = (String[]) legendList
322: .toArray(new String[legendList.size()]);
323: for (int i = 0; i < styles.length; i++) {
324: doc.insertString(doc.getLength(), legend[i], legendPane
325: .getStyle(styles[i]));
326: }
327: } catch (BadLocationException e) {
328: System.out.println("Unable to display legend properly");
329: }
330:
331: JPanel legendPanel = new JPanel(new FlowLayout());
332: legendPanel.add(legendPane);
333: return legendPanel;
334: }
335:
336: /**
337: * Initailizes the styles used in the legend.
338: *
339: * @param textPane the panel to be affected.
340: */
341: protected void initStylesForTextPane(JTextPane textPane) {
342:
343: Style def = StyleContext.getDefaultStyleContext().getStyle(
344: StyleContext.DEFAULT_STYLE);
345:
346: Style regular = textPane.addStyle("regular", def);
347: StyleConstants.setFontFamily(def, "SansSerif");
348:
349: Style s = textPane.addStyle("bold", regular);
350: StyleConstants.setBold(s, true);
351:
352: s = textPane.addStyle("black", regular);
353: StyleConstants.setForeground(s, Color.black);
354:
355: s = textPane.addStyle("green", regular);
356: StyleConstants.setForeground(s, Color.green);
357:
358: s = textPane.addStyle("red", regular);
359: StyleConstants.setForeground(s, Color.red);
360:
361: s = textPane.addStyle("yellow", regular);
362: StyleConstants.setForeground(s, Color.yellow);
363:
364: s = textPane.addStyle("orange", regular);
365: StyleConstants.setForeground(s, Color.orange);
366:
367: s = textPane.addStyle("blue", regular);
368: StyleConstants.setForeground(s, Color.blue);
369:
370: s = textPane.addStyle("purple", regular);
371: StyleConstants.setForeground(s, new Color(122, 22, 160));
372: }
373:
374: /**
375: * Creates a panel holding all buttons and initializes the buttons with ActionListeners.
376: *
377: * @return a panel ready to add to a component.
378: */
379: private JPanel createButtonPanel() {
380:
381: JPanel buttonPanel = new JPanel();
382:
383: JButton excludeButton = new JButton("Exclude");
384: excludeButton.addActionListener(new ActionListener() {
385: public void actionPerformed(ActionEvent e) {
386: TreePath[] selectedPaths = tree.getSelectionPaths();
387: if (selectedPaths == null) {
388: // nothing was selected when button was pushed
389: return;
390: }
391: for (int i = 0; i < selectedPaths.length; i++) {
392: // process the current selected node(s)
393: DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) selectedPaths[i]
394: .getLastPathComponent();
395: processExcludeButton(selectedNode);
396: paint(getGraphics());
397: }
398: }
399: });
400:
401: JButton includeButton = new JButton("Include");
402: includeButton.addActionListener(new ActionListener() {
403: public void actionPerformed(ActionEvent e) {
404: TreePath[] selectedPaths = tree.getSelectionPaths();
405: if (selectedPaths == null) {
406: // nothing was selected when button was pushed
407: return;
408: }
409: for (int i = 0; i < selectedPaths.length; i++) {
410: // process every child node of the current selected node(s)
411: DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) selectedPaths[i]
412: .getLastPathComponent();
413: processIncludeButton(selectedNode);
414: paint(getGraphics());
415: }
416: }
417: });
418:
419: JButton saveButton = new JButton("Save");
420: saveButton.addActionListener(new ActionListener() {
421: public void actionPerformed(ActionEvent e) {
422: processSaveButton();
423: }
424: });
425:
426: GridBagLayout gridbag = new GridBagLayout();
427: GridBagConstraints c = new GridBagConstraints();
428:
429: c.fill = GridBagConstraints.BOTH;
430: c.weightx = 1.0;
431:
432: buttonPanel.setLayout(new BorderLayout());
433:
434: c.gridwidth = GridBagConstraints.REMAINDER;
435:
436: JPanel topPanel = new JPanel(gridbag);
437: gridbag.setConstraints(excludeButton, c);
438: topPanel.add(excludeButton);
439: gridbag.setConstraints(includeButton, c);
440: topPanel.add(includeButton);
441: buttonPanel.add(topPanel, BorderLayout.NORTH);
442:
443: JPanel legendPanel = createLegend();
444: gridbag.setConstraints(legendPanel, c);
445: buttonPanel.add(legendPanel, BorderLayout.CENTER);
446:
447: JPanel bottomPanel = new JPanel(gridbag);
448: gridbag.setConstraints(saveButton, c);
449: bottomPanel.add(saveButton);
450: buttonPanel.add(bottomPanel, BorderLayout.SOUTH);
451:
452: return buttonPanel;
453: }
454:
455: /**
456: * Processes the clicking of the "Exclude" button. This method is recursive.
457: *
458: * @param treeNode the tree affected by the buton.
459: */
460: private void processExcludeButton(DefaultMutableTreeNode treeNode) {
461:
462: if (!treeNode.isLeaf()) {
463: Enumeration children = treeNode.children();
464: while (children.hasMoreElements()) {
465: DefaultMutableTreeNode nextChild = (DefaultMutableTreeNode) children
466: .nextElement();
467: processExcludeButton(nextChild);
468: }
469: } else {
470: MethodNode nextMethod = (MethodNode) treeNode
471: .getUserObject();
472: nextMethod
473: .setNewColor(MethodCategoryColor.EXCLUDED_INDIVIDUAL
474: .getColor());
475: }
476: }
477:
478: /**
479: * Processes the clicking of the "Include" button. This method is recursive.
480: *
481: * @param treeNode the tree affected by the buton.
482: */
483: private void processIncludeButton(DefaultMutableTreeNode treeNode) {
484:
485: if (!treeNode.isLeaf()) {
486: Enumeration children = treeNode.children();
487: while (children.hasMoreElements()) {
488: DefaultMutableTreeNode nextChild = (DefaultMutableTreeNode) children
489: .nextElement();
490: processIncludeButton(nextChild);
491: }
492: } else {
493: MethodNode nextMethod = (MethodNode) treeNode
494: .getUserObject();
495: // if node is an excluded individual method, then its color should toggle between
496: // excluded individual method and untested
497: if ((nextMethod.getOrigColor()
498: .equals(MethodCategoryColor.EXCLUDED_INDIVIDUAL
499: .getColor()))
500: && (nextMethod.getNewColor()
501: .equals(MethodCategoryColor.EXCLUDED_INDIVIDUAL
502: .getColor()))) {
503: nextMethod.setNewColor(MethodCategoryColor.UNTESTED
504: .getColor());
505: } else {
506: // else revert to the previous category
507: nextMethod.setNewColor(nextMethod.getOrigColor());
508: }
509: }
510: }
511:
512: /**
513: * Processes the clicking of the "Save" button.
514: */
515: private void processSaveButton() {
516: // writes out current configuration to XML files
517: String fileName = categories
518: .getFileName("excludedIndividualFile");
519:
520: try {
521: (new TreeWriter(treeModel)).store(new FileOutputStream(
522: new File(fileName)));
523: } catch (IOException e) {
524: System.out
525: .println("Unable to store individually excluded methods to "
526: + fileName);
527: }
528:
529: JOptionPane.showMessageDialog(frame,
530: "Excluded methods saved to\n" + fileName,
531: "JBlanket Notification", JOptionPane.PLAIN_MESSAGE);
532: }
533:
534: /**
535: * Provides the command line interface.
536: *
537: * @param args the command line arguments.
538: * @throws JBlanketException if cannot read the date from files.
539: */
540: public static void main(String args[]) throws JBlanketException {
541: main(java.util.Arrays.asList(args));
542: }
543:
544: /**
545: * Launches the application for excluding individual methods.
546: *
547: * @param args the command line arguments.
548: * @throws JBlanketException if errors occur.
549: */
550: public static void main(List args) throws JBlanketException {
551:
552: // Verbose mode
553: boolean verbose = false;
554: // format of final HTML report
555: String reportFormat = "frames";
556:
557: // Exclude one-line methods
558: boolean excludeOneLineMethods = false;
559: // Exclude constructors
560: boolean excludeConstructors = false;
561:
562: MethodCategories categories = MethodCategories.getInstance();
563:
564: // index of current command line arguments.
565: int i;
566: // Parses args into corresponsing variables.
567: for (i = 0; i < args.size(); ++i) {
568:
569: String argument = (String) args.get(i);
570:
571: if (argument.equals("-verbose")) {
572: verbose = (new Boolean((String) args.get(++i)))
573: .booleanValue();
574: } else if (argument.equals("-excludeOneLineMethods")) {
575: excludeOneLineMethods = (new Boolean((String) args
576: .get(++i))).booleanValue();
577: } else if (argument.equals("-excludeConstructors")) {
578: excludeConstructors = (new Boolean((String) args
579: .get(++i))).booleanValue();
580: } else if (argument.equals("-oneLineFile")) {
581: categories.addCategory("oneLineFile", (String) args
582: .get(++i));
583: } else if (argument.equals("-constructorFile")) {
584: categories.addCategory("constructorFile", (String) args
585: .get(++i));
586: } else if (argument.equals("-excludedIndividualFile")) {
587: categories.addCategory("excludedIndividualFile",
588: (String) args.get(++i));
589: } else if (argument.equals("-total.testedFile")) {
590: categories.addCategory("total.testedFile");
591: } else if (argument.equals("-total.untestedFile")) {
592: categories.addCategory("total.untestedFile");
593: } else {
594: System.out.println("Incorrect usage: " + argument);
595: System.exit(1);
596: }
597: }
598:
599: try {
600: // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
601: // UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
602: // UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
603: UIManager
604: .setLookAndFeel("com.sun.java.swing.plaf.motif.MotifLookAndFeel");
605: } catch (Exception e) {
606: // do nothing
607: }
608:
609: JFrame frame = new JFrame(
610: "JBlanket - Exclude Individual Methods");
611:
612: Container contentPane = frame.getContentPane();
613: contentPane.setLayout(new GridLayout(0, 1));
614: contentPane.add(new ExcludeIndividualMethodApp(verbose,
615: excludeOneLineMethods, excludeConstructors, frame));
616:
617: frame.addWindowListener(new WindowAdapter() {
618: public void windowClosing(WindowEvent e) {
619: System.exit(0);
620: }
621: });
622: frame.pack();
623: frame.setVisible(true);
624: }
625: }
|