001: package csdl.jblanket.methodset;
002:
003: import csdl.jblanket.modifier.MethodCollector;
004: import csdl.jblanket.util.XmlLabel;
005:
006: import java.io.IOException;
007: import java.io.InputStream;
008: import java.io.OutputStream;
009: import java.text.ParseException;
010: import java.util.ArrayList;
011: import java.util.Comparator;
012: import java.util.Date;
013: import java.util.Iterator;
014: import java.util.List;
015: import java.util.Set;
016: import java.util.TreeSet;
017:
018: import org.jdom.Document;
019: import org.jdom.Element;
020: import org.jdom.JDOMException;
021: import org.jdom.input.SAXBuilder;
022: import org.jdom.output.XMLOutputter;
023: import org.jdom.output.Format;
024:
025: /**
026: * Implements the MethodSet abstract data type, which provides a container for managing
027: * information about Java methods collected through dynamic analysis (JBlanket).
028: *
029: * @author Philip Johnson, Joy M. Agustin
030: * @version $Id: MethodSet.java,v 1.2 2005/03/07 01:49:08 timshadel Exp $
031: */
032: public class MethodSet {
033:
034: /** Holds the individual Method objects. */
035: private Set elements;
036:
037: private static Comparator methodInfoComparator = new Comparator() {
038: /**
039: * Compares the two objects (which are MethodInfos).
040: *
041: * @param o1 The first MethodInfo.
042: * @param o2 The second MethodInfo.
043: * @return Typical compare values.
044: */
045: public int compare(Object o1, Object o2) {
046: MethodInfo info1 = (MethodInfo) o1;
047: MethodInfo info2 = (MethodInfo) o2;
048: return info1.getMethodString().compareTo(
049: info2.getMethodString());
050: }
051: };
052:
053: /** Creates a new MethodSet. */
054: public MethodSet() {
055: this .elements = new TreeSet(methodInfoComparator);
056: }
057:
058: /**
059: * Adds a new Method instance to the MethodSet. If the Method instance already is in
060: * the set, it is not added.
061: *
062: * @param method The Method instance to be added.
063: * @return True if this MethodSet did not already contain the method.
064: */
065: public synchronized boolean add(MethodInfo method) {
066: return this .elements.add(method);
067: }
068:
069: /**
070: * Returns an iterator over this MethodSet. The elements are sorted by fully qualified
071: * class name.
072: *
073: * @return An iterator over the Methods in sorted order.
074: */
075: public synchronized Iterator iterator() {
076: return this .elements.iterator();
077: }
078:
079: /**
080: * Returns true if this MethodSet contains this Method instance.
081: *
082: * @param method The method to be found.
083: * @return True if found.
084: */
085: public synchronized boolean contains(MethodInfo method) {
086: return this .elements.contains(method);
087: }
088:
089: /**
090: * Returns true if this MethodSet has the same set of elements as the passed MethodSet.
091: *
092: * @param methodSet A MethodSet.
093: * @return True iff the two MethodSets have the same elements.
094: */
095: public synchronized boolean equals(MethodSet methodSet) {
096: return this .elements.equals(methodSet.elements);
097: }
098:
099: /**
100: * Removes the specified Method from this MethodSet, if it is present.
101: *
102: * @param method The method to be removed.
103: * @return True if it was present originally.
104: */
105: public synchronized boolean remove(MethodInfo method) {
106: return this .elements.remove(method);
107: }
108:
109: /**
110: * Returns true if this MethodSet is empty.
111: *
112: * @return True if empty.
113: */
114: public synchronized boolean isEmpty() {
115: return this .elements.isEmpty();
116: }
117:
118: /**
119: * Returns the number of elements in this MethodSet (its cardinality).
120: *
121: * @return The cardinality of the MethodSet.
122: */
123: public synchronized int size() {
124: return this .elements.size();
125: }
126:
127: /**
128: * Updates this MethodSet with the difference between this MethodSet and the passed
129: * MethodSet.
130: *
131: * @param methodSet The methodSet whose elements are to be removed from this methodSet.
132: * @return This MethodSet modified with the difference between the two.
133: */
134: public synchronized MethodSet difference(MethodSet methodSet) {
135: MethodSet diffSet = new MethodSet();
136: for (Iterator i = this .iterator(); i.hasNext();) {
137: MethodInfo method = (MethodInfo) i.next();
138: if (!methodSet.contains(method)) {
139: diffSet.add(method);
140: }
141: }
142: this .elements = diffSet.elements;
143: return this ;
144: }
145:
146: /**
147: * Updates this MethodSet with the intersection of this MethodSet and the passed
148: * MethodSet.
149: *
150: * @param methodSet The methodSet whose elements are to be removed from this methodSet.
151: * @return This MethodSet modified with the difference between the two.
152: */
153: public synchronized MethodSet intersection(MethodSet methodSet) {
154: MethodSet intersectSet = new MethodSet();
155: for (Iterator i = this .iterator(); i.hasNext();) {
156: MethodInfo method = (MethodInfo) i.next();
157: if (methodSet.contains(method)) {
158: intersectSet.add(method);
159: }
160: }
161: this .elements = intersectSet.elements;
162: return this ;
163: }
164:
165: /**
166: * Updates this MethodSet with the union of this MethodSet and the passed MethodSet.
167: *
168: * @param methodSet The methodSet whose elements this methodSet is unioned with.
169: * @return This MethodSet modified with the union of the two.
170: */
171: public synchronized MethodSet union(MethodSet methodSet) {
172: MethodSet unionSet = methodSet;
173: for (Iterator i = this .iterator(); i.hasNext();) {
174: MethodInfo method = (MethodInfo) i.next();
175: if (!methodSet.contains(method)) {
176: unionSet.add(method);
177: }
178: }
179: this .elements = unionSet.elements;
180: return this ;
181: }
182:
183: /**
184: * Adds the Method instances found in the passed stream to this MethodSet. The
185: * InputStream is assumed to contain a MethodSet in XML format that conforms to
186: * the MethodSet.dtd specification. The returned time stamp is formatted as
187: * "MMM dd, yyyy HH:mm:ss" where
188: * <p>
189: * MMM - month<br>
190: * dd - day<br>
191: * yyyy - year<br>
192: * HH - hour<br>
193: * mm - minutes<br>
194: * ss - seconds<br>
195: *
196: * @param stream The InputStream from which to construct the MethodSet.
197: * @return time stamp stored in file, null if there is none.
198: * @throws IOException If an error occurs during load.
199: * @throws ParseException If cannot parse time stamp from file.
200: */
201: public synchronized Date load(InputStream stream)
202: throws IOException, ParseException {
203:
204: SAXBuilder builder = new SAXBuilder();
205: Document methodSetDocument;
206: try {
207: methodSetDocument = builder.build(stream);
208: } catch (JDOMException e) {
209: throw new IOException(
210: "Error during load of MethodSet XML from inputstream.");
211: }
212:
213: // load method information
214: Element methodSetElement = methodSetDocument.getRootElement();
215: for (Iterator i = methodSetElement.getChildren().iterator(); i
216: .hasNext();) {
217: Element methodElement = (Element) i.next();
218: String className = methodElement
219: .getAttributeValue(XmlLabel.CLASS_ATTRIBUTE
220: .toString());
221: String methodName = methodElement
222: .getAttributeValue(XmlLabel.METHOD_ATTRIBUTE
223: .toString());
224: List parameterTypeList = new ArrayList();
225:
226: for (Iterator j = methodElement.getChildren(
227: XmlLabel.PARAMETER.toString()).iterator(); j
228: .hasNext();) {
229: Element parameter = (Element) j.next();
230: parameterTypeList.add(parameter
231: .getAttributeValue(XmlLabel.TYPE_ATTRIBUTE
232: .toString()));
233: }
234:
235: this .elements.add(new MethodInfo(className, methodName,
236: parameterTypeList));
237: }
238:
239: // return timeStamp stored in file
240: String timeStamp = methodSetElement
241: .getAttributeValue(XmlLabel.TIMESTAMP_ATTRIBUTE
242: .toString());
243: return MethodCollector.getDateFormat().parse(timeStamp);
244: }
245:
246: /**
247: * Writes this MethodSet in XML format to the passed output stream. If
248: * <code>className</code> or <code>timeStamp</code> is null, "class" or "timestamp"
249: * attribute for "MethodSet" in output file is set to "" (empty String). This
250: * behavior can occur during the XML transformation for the JBlanket report.
251: *
252: * @param ostream the OutputStream the MethodSet will be written to.
253: * @param className the name of the class, if one exists.
254: * @param timeStamp the time stamp to include in this file.
255: * @exception IOException if errors during writing.
256: */
257: public synchronized void store(OutputStream ostream,
258: String className, Date timeStamp) throws IOException {
259: XMLOutputter outputter = new XMLOutputter(Format
260: .getPrettyFormat());
261: Document methodSetDocument = new Document(createRoot(className,
262: timeStamp));
263: outputter.output(methodSetDocument, ostream);
264: }
265:
266: /**
267: * Creates a Document object from this MethodSet.
268: *
269: * @param className the name of the class, if one exists.
270: * @param timeStamp the time stamp to include in this file.
271: * @return the the new Document.
272: */
273: public synchronized Element createRoot(String className,
274: Date timeStamp) {
275:
276: // create the root element in the document
277: Element methodSetRoot = new Element(XmlLabel.METHODSET
278: .toString());
279:
280: if (className != null) {
281: methodSetRoot.setAttribute(XmlLabel.NAME_ATTRIBUTE
282: .toString(), className);
283: } else {
284: methodSetRoot.setAttribute(XmlLabel.NAME_ATTRIBUTE
285: .toString(), "");
286: }
287: methodSetRoot.setAttribute(XmlLabel.SIZE_ATTRIBUTE.toString(),
288: (new Integer(this .elements.size())).toString());
289:
290: if (timeStamp != null) {
291: String timeStampString = MethodCollector.getDateFormat()
292: .format(timeStamp);
293: methodSetRoot.setAttribute(XmlLabel.TIMESTAMP_ATTRIBUTE
294: .toString(), timeStampString);
295: } else {
296: methodSetRoot.setAttribute(XmlLabel.TIMESTAMP_ATTRIBUTE
297: .toString(), "");
298: }
299:
300: // process each element
301: for (Iterator i = this .elements.iterator(); i.hasNext();) {
302:
303: MethodInfo method = (MethodInfo) i.next();
304:
305: // add the method element in XML file
306: Element methodElement = new Element(XmlLabel.METHOD
307: .toString());
308: methodSetRoot.addContent(methodElement);
309: methodElement.setAttribute(XmlLabel.CLASS_ATTRIBUTE
310: .toString(), method.getClassName());
311: methodElement.setAttribute(XmlLabel.METHOD_ATTRIBUTE
312: .toString(), method.getMethodName());
313:
314: // process each parameter type
315: for (Iterator j = method.getParameterTypeList().iterator(); j
316: .hasNext();) {
317: String parameterType = (String) j.next();
318: Element parameterElement = new Element(
319: XmlLabel.PARAMETER.toString());
320: methodElement.addContent(parameterElement);
321: parameterElement.setAttribute(XmlLabel.TYPE_ATTRIBUTE
322: .toString(), parameterType);
323: }
324: }
325: return methodSetRoot;
326: }
327:
328: /**
329: * Provides a readable representation of the MethodSet.
330: *
331: * @return The MethodSet as a string.
332: */
333: public synchronized String toString() {
334: return "[MethodSet " + this .elements + "]";
335: }
336: }
|