001: /*
002: * @(#)ITFProblemManager.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Part of the GroboUtils package at:
009: * http://groboutils.sourceforge.net
010: *
011: * Permission is hereby granted, free of charge, to any person obtaining a
012: * copy of this software and associated documentation files (the "Software"),
013: * to deal in the Software without restriction, including without limitation
014: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
015: * and/or sell copies of the Software, and to permit persons to whom the
016: * Software is furnished to do so, subject to the following conditions:
017: *
018: * The above copyright notice and this permission notice shall be included in
019: * all copies or substantial portions of the Software.
020: *
021: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
022: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
023: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
024: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
025: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
026: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
027: * DEALINGS IN THE SOFTWARE.
028: */
029: package net.sourceforge.groboutils.pmti.v1.itf;
030:
031: import net.sourceforge.groboutils.pmti.v1.IIssue;
032: import net.sourceforge.groboutils.pmti.v1.IAttribute;
033: import net.sourceforge.groboutils.pmti.v1.IIssueState;
034: import net.sourceforge.groboutils.pmti.v1.IAttributeSet;
035: import net.sourceforge.groboutils.pmti.v1.IAttributeInfo;
036: import net.sourceforge.groboutils.pmti.v1.IIssueTypeInfo;
037: import net.sourceforge.groboutils.pmti.v1.IEditableIssue;
038: import net.sourceforge.groboutils.pmti.v1.IListAttribute;
039: import net.sourceforge.groboutils.pmti.v1.IProblemManager;
040: import net.sourceforge.groboutils.pmti.v1.IProblemManagerInfo;
041: import net.sourceforge.groboutils.pmti.v1.ProblemManagerException;
042:
043: import net.sourceforge.groboutils.pmti.v1.defimpl.AbstractIssue;
044: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultAttribute;
045: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultAttributeSet;
046: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultAttributeInfo;
047: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultIssueState;
048: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultIssueTypeInfo;
049: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultListAttribute;
050: import net.sourceforge.groboutils.pmti.v1.defimpl.DefaultProblemManagerInfo;
051:
052: import net.sourceforge.groboutils.pmti.v1.itf.parser.IParserGenerator;
053: import net.sourceforge.groboutils.pmti.v1.itf.parser.IParserCollator;
054: import net.sourceforge.groboutils.pmti.v1.itf.parser.IParser;
055:
056: import java.util.Hashtable;
057: import java.util.Vector;
058: import java.util.Enumeration;
059:
060: /**
061: * A read-only ProblemManager for the ITF section of the PMTI framework. No
062: * states are supported.
063: *
064: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
065: * @version $Date: 2003/02/10 22:51:59 $
066: * @since July 12, 2002
067: */
068: public class ITFReadProblemManager implements IProblemManager {
069:
070: private Hashtable issueIdToIssue = new Hashtable();
071: private IIssueTypeInfo typeInfo;
072: private IProblemManagerInfo pmInfo;
073: private IAttributeInfo testAttribInfo;
074:
075: public ITFReadProblemManager(IParserGenerator generator) {
076: this (generator.createParsers());
077: }
078:
079: public ITFReadProblemManager(IParser parsers) {
080: this (new IParser[] { parsers });
081: }
082:
083: public ITFReadProblemManager(IParser[] parsers) {
084: if (parsers == null) {
085: throw new IllegalArgumentException("no null arguments");
086: }
087:
088: for (int i = 0; i < parsers.length; ++i) {
089: loadParser(parsers[i]);
090: }
091:
092: setupInfo();
093: }
094:
095: /**
096: * Returns a list of all issue IDs known by the PMT. This may be an
097: * extremely expensive operation, depending on the size of the underlying
098: * system. This call must never return <tt>null</tt>.
099: */
100: public String[] getIssueIDs()
101: {
102: // *Sigh* JDK 1.1 again...
103: String ss[];
104: synchronized (this .issueIdToIssue)
105: {
106: ss = new String[ this .issueIdToIssue.size() ];
107: Enumeration enum = this .issueIdToIssue.keys();
108: for (int i = 0; enum.hasMoreElements(); ++i)
109: {
110: String s = (String)enum.nextElement();
111: ss[i] = s;
112: }
113: }
114: return ss;
115: }
116:
117: /**
118: * Returns all issue IDs that match the given 'template'. A template is
119: * an issue where all <tt>null</tt> values are considered 'wildcards',
120: * so that a <tt>null</tt> state, for instance, would match for any
121: * state object. May change the template in the future to a different
122: * but similar type to allow for regular-expressions.
123: */
124: public String[] getIssueIDsForTemplate(IIssue issue) {
125: if (issue == null) {
126: return new String[0];
127: }
128:
129: if (issue.getType() != null) {
130: // optimization:
131: // there is only one type supported now, so if that type is
132: // not the template type, then there is no match, otherwise
133: // there is no change to the candidate list.
134: if (!issue.getType().equals(typeInfo.getName())) {
135: return new String[0];
136: }
137: }
138:
139: if (issue.getState() != null) {
140: // optimization:
141: // there is only one state supported now, which is null.
142: // So since the template expects a specific state which is
143: // not null, there can be no possible match.
144: return new String[0];
145: }
146:
147: Enumeration currentCandidates;
148: if (issue.getID() != null) {
149: Vector v = new Vector();
150: v.addElement(getIssueByID(issue.getID()));
151: currentCandidates = v.elements();
152: } else {
153: currentCandidates = issueIdToIssue.elements();
154: }
155:
156: if (issue.getShortDescription() != null) {
157: String desc = issue.getShortDescription();
158: Vector matches = new Vector();
159: while (currentCandidates.hasMoreElements()) {
160: IIssue check = (IIssue) currentCandidates.nextElement();
161: if (desc.equals(check.getShortDescription())) {
162: matches.addElement(check);
163: }
164: }
165:
166: // update our valid match list
167: currentCandidates = matches.elements();
168: }
169:
170: if (issue.getAttributes() != null) {
171: // *sigh* there's the major work...
172: IAttributeSet set = issue.getAttributes();
173: Vector matches = new Vector();
174: while (currentCandidates.hasMoreElements()) {
175: IIssue check = (IIssue) currentCandidates.nextElement();
176: if (attributesMatch(set, check.getAttributes())) {
177: matches.addElement(check);
178: }
179: }
180: currentCandidates = matches.elements();
181: }
182:
183: Vector v = new Vector();
184: while (currentCandidates.hasMoreElements()) {
185: v.addElement(((IIssue) currentCandidates.nextElement())
186: .getID());
187: }
188: String s[] = new String[v.size()];
189: v.copyInto(s);
190: return s;
191: }
192:
193: /**
194: * Returns the issue associated with the given unique issue ID. If no such
195: * issue exists, then <tt>null</tt> is returned. Note that the returned
196: * element is a non-editable version of the issue.
197: */
198: public IIssue getIssueByID(String id) {
199: return (IIssue) this .issueIdToIssue.get(id);
200: }
201:
202: /**
203: * Given the real issue, returns the editable version of the issue.
204: */
205: public IEditableIssue editIssue(IIssue issue)
206: throws ProblemManagerException {
207: throw new ProblemManagerException("edit not allowed");
208: }
209:
210: /**
211: * Creates a new issue of the given type. If <tt>type</tt> is
212: * <tt>null</tt>, then a new issue of the default issue type is
213: * created and returned.
214: */
215: public IEditableIssue createIssue(String type)
216: throws ProblemManagerException {
217: throw new ProblemManagerException("create not allowed");
218: }
219:
220: /**
221: * Returns all meta-data for this problem management tracker.
222: */
223: public IProblemManagerInfo getProblemManagerInfo() {
224: return this .pmInfo;
225: }
226:
227: //-------------------------------------------------------------------------
228:
229: protected void loadParser(IParser p) {
230: ITestIssueRecord[] tir = p.parse();
231: if (tir != null) {
232: for (int i = 0; i < tir.length; ++i) {
233: addTestIssueRecord(tir[i]);
234: }
235: }
236: }
237:
238: protected void addTestIssueRecord(ITestIssueRecord tir) {
239: if (tir != null) {
240: String id = tir.getIssueRecord().getID();
241: IIssue issue = (IIssue) this .issueIdToIssue.get(id);
242: // add the test record to the issue - null issues handled correctly
243: issue = updateITFIssue(issue, tir);
244: this .issueIdToIssue.put(id, issue);
245: }
246: }
247:
248: protected IIssue createNewITFIssue(ITestIssueRecord tir) {
249: String id = tir.getIssueRecord().getID();
250: String desc = tir.getIssueRecord().getDescription();
251:
252: return createITFIssue(id, desc, new ITestRecord[] { tir
253: .getTestRecord(), });
254: }
255:
256: protected IIssue updateITFIssue( IIssue orig, ITestIssueRecord addTir )
257: {
258: if (orig == null)
259: {
260: return createNewITFIssue( addTir );
261: }
262: if (!orig.getID().equals( addTir.getIssueRecord().getID() ))
263: {
264: throw new IllegalArgumentException(
265: "original issue id ("+orig.getID()+
266: ") not same as input ITestIssueRecord ("+
267: addTir.getIssueRecord().getID()+")" );
268: }
269: IAttribute attrib = orig.getAttributes().getAttribute(
270: this .testAttribInfo.getName() );
271: if (attrib == null)
272: {
273: throw new IllegalArgumentException(
274: "invalid issue: does not contain an attribute with name "+
275: this .testAttribInfo.getName() );
276: }
277: ITestRecord[] tr;
278: if (attrib instanceof IListAttribute)
279: {
280: Vector v = new Vector();
281: Enumeration enum = ((IListAttribute)attrib).getValues();
282: while (enum.hasMoreElements())
283: {
284: v.addElement( (ITestRecord)enum.nextElement() );
285: }
286: v.addElement( addTir.getTestRecord() );
287: tr = new ITestRecord[ v.size() ];
288: v.copyInto( tr );
289: }
290: else
291: {
292: tr = new ITestRecord[] {
293: (ITestRecord)attrib.getValue(),
294: addTir.getTestRecord()
295: };
296: }
297:
298:
299: String id = orig.getID();
300: String desc = orig.getShortDescription();
301: if (desc == null)
302: {
303: desc = addTir.getIssueRecord().getDescription();
304: }
305:
306: return createITFIssue( id, desc, tr );
307: }
308:
309: protected IIssue createITFIssue(String id, String desc,
310: ITestRecord[] tr) {
311: IAttribute a1 = new DefaultListAttribute(tr,
312: this .testAttribInfo);
313:
314: IAttributeSet as = new DefaultAttributeSet(
315: new IAttribute[] { a1, });
316:
317: IIssue issue = new ITFIssue(id, this .typeInfo.getName(), desc,
318: null, as);
319: return issue;
320: }
321:
322: protected void setupInfo() {
323: if (typeInfo != null || pmInfo != null
324: || testAttribInfo != null) {
325: throw new IllegalStateException("already setup info.");
326: }
327:
328: String defaultIssueName = "test issue";
329:
330: this .testAttribInfo = new DefaultAttributeInfo("test",
331: "test that covers this issue",
332: new Class[] { ITestRecord.class });
333:
334: this .typeInfo = new DefaultIssueTypeInfo(defaultIssueName,
335: new IAttributeInfo[] { this .testAttribInfo }, null,
336: null, null);
337:
338: this .pmInfo = new DefaultProblemManagerInfo(defaultIssueName,
339: new IIssueTypeInfo[] { this .typeInfo });
340: }
341:
342: protected boolean attributesMatch(IAttributeSet check,
343: IAttributeSet verify) {
344: IAttribute[] checkAA = check.getAttributes();
345: IAttribute[] verifyAA = verify.getAttributes();
346:
347: // Note: for a match to be valid, all of the check attributes
348: // must exist correctly in the verify attributes, but if there are
349: // verify attributes which are not in check, then it's still a
350: // match.
351: for (int checkIndex = 0; checkIndex < checkAA.length; ++checkIndex) {
352: IAttribute checkA = checkAA[checkIndex];
353: if (checkA == null) {
354: // ignore this one - assume it's a match
355: continue;
356: }
357: boolean notfound = true;
358:
359: for (int verifyIndex = 0; verifyIndex < verifyAA.length; ++verifyIndex) {
360: IAttribute verifyA = verifyAA[verifyIndex];
361: if (verifyA == null) {
362: // this attribute has already been matched, so skip to
363: // the next one.
364: continue;
365: }
366:
367: if (attributeMatch(checkA, verifyA)) {
368: // At this point, we have verified that both attributes do
369: // indeed match each other. Mark it as so.
370: notfound = false;
371: verifyAA[verifyIndex] = null;
372: break;
373: }
374: }
375: if (notfound) {
376: // did not find a match for the check attribute in any
377: // of the verify attributes.
378: return false;
379: }
380: }
381:
382: return true;
383: }
384:
385: protected boolean attributeMatch(IAttribute checkA,
386: IAttribute verifyA) {
387: // If checkA specifies an Info, then the infos must
388: // match, otherwise, it will match any attribute.
389: if (checkA.getInfo() != null) {
390: if (!verifyA.getInfo().equals(checkA.getInfo())) {
391: // cannot possibly be a match
392: return false;
393: }
394: }
395:
396: // If checkA specifies a value, then both values must match,
397: // otherwise it will match any value.
398: if (checkA.getValue() != null) {
399: // if they're both list attributes, then check need to only
400: // contain a sub-set of the verify elements.
401: if (checkA instanceof IListAttribute) {
402: if (!(verifyA instanceof IListAttribute)) {
403: // cannot possibly be a match
404: return false;
405: }
406: IListAttribute verifyLA = (IListAttribute) verifyA;
407: Enumeration checkE = ((IListAttribute) checkA)
408: .getValues();
409: while (checkE.hasMoreElements()) {
410: Object checkO = checkE.nextElement();
411: Enumeration verifyE = verifyLA.getValues();
412: boolean enumNotfound = true;
413: while (verifyE.hasMoreElements()) {
414: if (checkO.equals(verifyE.nextElement())) {
415: // matched this value.
416: enumNotfound = false;
417: break;
418: }
419: }
420: if (enumNotfound) {
421: // no match for the check's value.
422: return false;
423: }
424: }
425: } else {
426: if (!checkA.getValue().equals(verifyA.getValue())) {
427: // values don't match.
428: return false;
429: }
430: }
431: }
432:
433: // We have verified that both attributes do indeed match each other.
434: return true;
435: }
436:
437: private static class ITFIssue extends AbstractIssue {
438: public ITFIssue(String i, String t, String d, IIssueState s,
439: IAttributeSet a) {
440: super (i, t, d, s, a);
441: }
442:
443: public IIssue reload() throws ProblemManagerException {
444: // assumption is that this problem manager is read-only, so
445: // it won't change underneath us.
446:
447: // Yes, that's not necessarily true, but it keeps the
448: // implementation simple.
449:
450: return this;
451: }
452: }
453: }
|