001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.nbbuild;
043:
044: import java.io.*;
045: import java.util.*;
046: import java.util.Date;
047:
048: /** Represents on issue in issuezilla.
049: * Created by {@link Issuezilla#getBug}
050: *
051: * @author Ivan Bradac, refactored by Jaroslav Tulach
052: */
053: public final class Issue extends Object implements Comparable {
054: //static final String ENHANCEMENT = "ENHANCEMENT";
055: static final String ISSUE_TYPE = "issue_type";
056: static final String SHORT_DESC = "short_desc";
057: static final String LONG_DESC = "long_desc";
058: static final String COMMENT = "comment";
059: static final String ISSUE_ID = "issue_id";
060: static final String ISSUE_STATUS = "issue_status";
061: static final String RESOLUTION = "resolution";
062: static final String COMPONENT = "component";
063: static final String REPORTER = "reporter";
064: static final String VERSION = "version";
065: static final String SUBCOMPONENT = "subcomponent";
066: static final String REP_PLATFORM = "rep_platform";
067: static final String OP_SYS = "op_sys";
068: static final String PRIORITY = "priority";
069: static final String ASSIGNED_TO = "assigned_to";
070: static final String CC = "cc";
071: static final String DEPENDS_ON = "dependson";
072: static final String BLOCKS = "blocks";
073: static final String CREATED = "creation_ts";
074: static final String VOTES = "votes";
075: static final String KEYWORDS = "keywords";
076: static final String STATUS_WHITEBOARD = "status_whiteboard";
077:
078: /** The target milestone attribute name. */
079: static final String TARGET_MILESTONE = "target_milestone";
080:
081: /** Name of the attribute containing the long_desc as a list */
082: static final String LONG_DESC_LIST = "long_desc_list";
083:
084: private Map<String, Object> attributes = new HashMap<String, Object>(
085: 49);
086:
087: /**
088: * Gets the id as an Integer.
089: *
090: * @return the issue_id as
091: */
092: public int getId() {
093: Object id = getAttribute(ISSUE_ID);
094: try {
095: return Integer.parseInt((String) id);
096: } catch (Exception ex) {
097: return -1;
098: }
099: }
100:
101: /** Who is assigned to this bug.
102: * @return name of person assigned to this bug
103: */
104: public String getAssignedTo() {
105: return string(ASSIGNED_TO);
106: }
107:
108: /** Who reported the bug.
109: * @return name of the reporter
110: */
111: public String getReportedBy() {
112: return string(REPORTER);
113: }
114:
115: /** Everyone who is interested in the issue.
116: * @return array of names or empty array if nobody is
117: */
118: public String[] getObservedBy() {
119: List<?> l = (List<?>) getAttribute(CC);
120: if (l != null) {
121: return l.toArray(new String[0]);
122: } else {
123: return new String[0];
124: }
125: }
126:
127: /** Status of the bug, verified, etc.
128: * @return textual name of the status.
129: */
130: public String getStatus() {
131: return string(ISSUE_STATUS);
132: }
133:
134: /** Resolution: Fixed, etc...
135: * @return textual name of resolution.
136: */
137: public String getResolution() {
138: return string(RESOLUTION);
139: }
140:
141: /** Type of the issue: Bug, Enhancement, Task, etc...
142: * @return textual name of issue type
143: */
144: public String getType() {
145: return string(ISSUE_TYPE);
146: }
147:
148: /** Priority of the issue.
149: * @return integer describing priority, -1 if unknown
150: */
151: public int getPriority() {
152: String s = string(PRIORITY);
153: if (s.length() == 2 && s.charAt(0) == 'P') {
154: return s.charAt(1) - '0';
155: } else {
156: return -1;
157: }
158: }
159:
160: /** A time when this issue has been created.
161: * @return the date or begining of epoch if wrongly defined
162: */
163: public Date getCreated() {
164: Date d = (Date) getAttribute(CREATED);
165: return d == null ? new Date(0) : d;
166: }
167:
168: /** The summary or short description of the bug.
169: * @return string
170: */
171: public String getSummary() {
172: return string(SHORT_DESC);
173: }
174:
175: /** Getter of descriptions.
176: * @return array of descriptions
177: */
178: public Description[] getDescriptions() {
179: Object obj = getAttribute(LONG_DESC_LIST);
180: if (obj == null) {
181: return new Description[0];
182: }
183:
184: return ((List<?>) obj).toArray(new Description[0]);
185: }
186:
187: /** A list of bugs that depends on this one.
188: * @return array of integer numbers of those bugs or empty array
189: */
190: public int[] getDependsOn() {
191: return ints(DEPENDS_ON);
192: }
193:
194: /** A list of bugs that this issue blocks.
195: * @return array of integer numbers of those bugs or empty array
196: */
197: public int[] getBlocks() {
198: return ints(BLOCKS);
199: }
200:
201: /** Name of the milestone this issue should be resolved in.
202: * @return string name
203: */
204: public String getTargetMilestone() {
205: return string(TARGET_MILESTONE);
206: }
207:
208: /** Name of the component this issue belongs to.
209: * @return string name
210: */
211: public String getComponent() {
212: return string(COMPONENT);
213: }
214:
215: /** Name of subcomponent this issue belongs to.
216: * @return string name
217: */
218: public String getSubcomponent() {
219: return string(SUBCOMPONENT);
220: }
221:
222: /**
223: * @deprecated Use <code>getWhiteboardAttribute("duration")</code> instead.
224: */
225: @Deprecated
226: public String getDuration() {
227: String val = getWhiteboardAttribute("duration");
228: return val == null ? "" : val;
229: }
230:
231: /**
232: * Get status white board attribute. Status whiteboard attributes
233: * are convetionally defined in form <tt>name:WORD=value:WORD</tt> e.g.
234: * <tt>duration=50</tt> or <tt>lines=1000</tt>.
235: * @param attribute name
236: * @return token representing attribute value or <code>null</code>
237: */
238: public final String getWhiteboardAttribute(String attribute) {
239: String ret = null;
240: String swb = string(STATUS_WHITEBOARD);
241: StringTokenizer st = new StringTokenizer(swb);
242: while (st.hasMoreTokens()) {
243: String token = st.nextToken();
244: if (token.startsWith(attribute + '=')) {
245: ret = token.substring(token.indexOf('=') + 1);
246: break;
247: }
248: }
249: return ret;
250: }
251:
252: /** Number of votes for given component.
253: * @return integer representing number of votes or 0 is no votes present
254: */
255: public int getVotes() {
256: try {
257: String s = string(VOTES);
258: return Integer.parseInt(s);
259: } catch (Exception ex) {
260: return 0;
261: }
262: }
263:
264: /** All keywords of the issue.
265: * @return Keywords deliminated by comma or empty string
266: */
267: public String getKeywords() {
268: try {
269: return string(KEYWORDS);
270: } catch (Exception ex) {
271: return "";
272: }
273: }
274:
275: /** Check if the this issue has the specified keyword
276: * @return <code>true</code> if specified keyword is set in this issue,
277: * otherwise <code>false</code>.
278: */
279: public boolean containsKeyword(String keyword) {
280: StringTokenizer tokenizer = new StringTokenizer(getKeywords());
281: while (tokenizer.hasMoreTokens()) {
282: String current = tokenizer.nextToken();
283: if (current.equals(keyword))
284: return true;
285: }
286: return false;
287: }
288:
289: /** Is this bug actually an enhancement?
290: * @return <CODE>true</CODE> if this is enhancement, <CODE>false</CODE> otherwise
291: *
292: public boolean isEnhancement() {
293: if (attributes == null) {
294: return false;
295: }
296: String s = (String) getAttribute(ISSUE_TYPE);
297: return (s == null) ? false : s.equals(ENHANCEMENT);
298: }
299: */
300:
301: /** Getter to return string for given attribute.
302: */
303: private String string(String name) {
304: Object o = getAttribute(name);
305: return o instanceof String ? (String) o : "";
306: }
307:
308: /** Getter for array of integers.
309: */
310: private int[] ints(String name) {
311: List l = (List) getAttribute(name);
312: if (l == null) {
313: return new int[0];
314: }
315:
316: int[] arr = new int[l.size()];
317: for (int i = 0; i < arr.length; i++) {
318: arr[i] = Integer.parseInt((String) l.get(i));
319: }
320: return arr;
321: }
322:
323: /** Package private getter, it is expected to add getter for useful
324: * issues.
325: */
326: Object getAttribute(String name) {
327: if (name.equals(LONG_DESC)) {
328: return formatLongDescriptions();
329: } else {
330: return attributes.get(name);
331: }
332: }
333:
334: /** Setter of values, package private. */
335: void setAttribute(String name, Object value) {
336: attributes.put(name, value);
337: }
338:
339: /**
340: * Gets the name/value pairs of the bug attributes as a Map.
341: *
342: * @return the name/value pairs of the attributes
343: */
344: private Map attributes() {
345: return attributes;
346: }
347:
348: /** Converts the object to textual representation.
349: * @return a text description of the issue
350: */
351: public String toString() {
352: StringBuffer buffer;
353: if (attributes == null) {
354: return "Empty BugBase";
355: }
356: Iterator it = attributes.entrySet().iterator();
357: buffer = new StringBuffer();
358: buffer.append(this .getClass().getName()
359: + " containing these name/value attribute pairs:\n");
360: while (it.hasNext()) {
361: Map.Entry entry = (Map.Entry) it.next();
362: buffer.append("NAME : " + entry.getKey() + "\n");
363: buffer.append("VALUE : " + entry.getValue() + "\n");
364: }
365: return buffer.toString();
366: }
367:
368: /** Compares issues by their ID
369: */
370: public int compareTo(Object o) {
371: Issue i = (Issue) o;
372: return getId() - i.getId();
373: }
374:
375: /**
376: * Formats the list of long_desc's into one String
377: *
378: * @return the long descriptions in one String
379: */
380: private String formatLongDescriptions() {
381: if (attributes.get(Issue.LONG_DESC) == null) {
382: StringBuffer buffer = new StringBuffer("");
383: Object obj = getAttribute(LONG_DESC_LIST);
384: List descriptions;
385: if (obj == null) {
386: return null;
387: }
388: descriptions = (List) obj;
389: Iterator it = descriptions.iterator();
390: while (it.hasNext()) {
391: Description ld = (Description) it.next();
392: buffer.append(ld.toString());
393: }
394: attributes.put(LONG_DESC, buffer.toString());
395: }
396: return attributes.get(LONG_DESC).toString();
397: }
398:
399: /**
400: * Long description of Issues.
401: */
402: public final static class Description {
403: static final String WHO = "who";
404: static final String ISSUE_WHEN = "issue_when";
405: static final String BODY = "body";
406: static final String THETEXT = "thetext";
407:
408: /** Holds value of property who. */
409: private String who;
410:
411: /** Holds value of property issue_when. */
412: private Date when;
413:
414: /** Holds value of property thetext. */
415: private String body;
416:
417: /** Name of the author of the issue.
418: * @return Value of property who.
419: */
420: public String getWho() {
421: return who;
422: }
423:
424: /** Setter for property who.
425: * @param who New value of property who.
426: */
427: void setWho(String who) {
428: this .who = who;
429: }
430:
431: /** When this comment has been added.
432: * @return Value of property issue_when.
433: */
434: public java.util.Date getWhen() {
435: return when;
436: }
437:
438: /** Setter for property issue_when.
439: * @param issue_when New value of property issue_when.
440: */
441: void setIssueWhen(Date when) {
442: this .when = when;
443: }
444:
445: /** The actual text of the issue.
446: * @return Value of property thetext.
447: */
448: public String getBody() {
449: return body;
450: }
451:
452: /** Textual description.
453: * @return string representation of the description.
454: */
455: public String toString() {
456: StringBuffer buffer = new StringBuffer();
457: buffer.append(getWho());
458: buffer.append(", ");
459: buffer.append(getWhen());
460: buffer.append(" : \n");
461: buffer.append(getBody());
462: buffer.append("\n\n");
463: return buffer.toString();
464: }
465:
466: /** Setter for property thetext.
467: * @param thetext New value of property thetext.
468: */
469: void setBody(String body) {
470: this .body = body;
471: }
472:
473: void setAtribute(String name, String value) {
474: if (name.equalsIgnoreCase(WHO)) {
475: setWho(value);
476: } else if (name.equalsIgnoreCase(BODY)
477: || name.equalsIgnoreCase(THETEXT)) {
478: setBody(value);
479: }
480: }
481:
482: private String getAttribute(String name) {
483: if (name.equalsIgnoreCase(WHO)) {
484: return who;
485: } else if (name.equalsIgnoreCase(BODY)
486: || name.equalsIgnoreCase(THETEXT)) {
487: return body;
488: } else {
489: return null;
490: }
491: }
492:
493: }
494:
495: }
|