diff options
Diffstat (limited to 'src/main/java/com/gitblit/models/IssueModel.java')
-rw-r--r-- | src/main/java/com/gitblit/models/IssueModel.java | 532 |
1 files changed, 532 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/models/IssueModel.java b/src/main/java/com/gitblit/models/IssueModel.java new file mode 100644 index 00000000..c9038913 --- /dev/null +++ b/src/main/java/com/gitblit/models/IssueModel.java @@ -0,0 +1,532 @@ +/*
+ * Copyright 2012 gitblit.com.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.gitblit.models;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.gitblit.utils.ArrayUtils;
+import com.gitblit.utils.StringUtils;
+import com.gitblit.utils.TimeUtils;
+
+/**
+ * The Gitblit Issue model, its component classes, and enums.
+ *
+ * @author James Moger
+ *
+ */
+public class IssueModel implements Serializable, Comparable<IssueModel> {
+
+ private static final long serialVersionUID = 1L;
+
+ public String id;
+
+ public Type type;
+
+ public Status status;
+
+ public Priority priority;
+
+ public Date created;
+
+ public String summary;
+
+ public String description;
+
+ public String reporter;
+
+ public String owner;
+
+ public String milestone;
+
+ public List<Change> changes;
+
+ public IssueModel() {
+ // the first applied change set the date appropriately
+ created = new Date(0);
+
+ type = Type.Defect;
+ status = Status.New;
+ priority = Priority.Medium;
+
+ changes = new ArrayList<Change>();
+ }
+
+ public String getStatus() {
+ String s = status.toString();
+ if (!StringUtils.isEmpty(owner))
+ s += " (" + owner + ")";
+ return s;
+ }
+
+ public boolean hasLabel(String label) {
+ return getLabels().contains(label);
+ }
+
+ public List<String> getLabels() {
+ List<String> list = new ArrayList<String>();
+ String labels = null;
+ for (Change change : changes) {
+ if (change.hasField(Field.Labels)) {
+ labels = change.getString(Field.Labels);
+ }
+ }
+ if (!StringUtils.isEmpty(labels)) {
+ list.addAll(StringUtils.getStringsFromValue(labels, " "));
+ }
+ return list;
+ }
+
+ public Attachment getAttachment(String name) {
+ Attachment attachment = null;
+ for (Change change : changes) {
+ if (change.hasAttachments()) {
+ Attachment a = change.getAttachment(name);
+ if (a != null) {
+ attachment = a;
+ }
+ }
+ }
+ return attachment;
+ }
+
+ public List<Attachment> getAttachments() {
+ List<Attachment> list = new ArrayList<Attachment>();
+ for (Change change : changes) {
+ if (change.hasAttachments()) {
+ list.addAll(change.attachments);
+ }
+ }
+ return list;
+ }
+
+ public void applyChange(Change change) {
+ if (changes.size() == 0) {
+ // first change created the issue
+ created = change.created;
+ }
+ changes.add(change);
+
+ if (change.hasFieldChanges()) {
+ for (FieldChange fieldChange : change.fieldChanges) {
+ switch (fieldChange.field) {
+ case Id:
+ id = fieldChange.value.toString();
+ break;
+ case Type:
+ type = IssueModel.Type.fromObject(fieldChange.value);
+ break;
+ case Status:
+ status = IssueModel.Status.fromObject(fieldChange.value);
+ break;
+ case Priority:
+ priority = IssueModel.Priority.fromObject(fieldChange.value);
+ break;
+ case Summary:
+ summary = fieldChange.value.toString();
+ break;
+ case Description:
+ description = fieldChange.value.toString();
+ break;
+ case Reporter:
+ reporter = fieldChange.value.toString();
+ break;
+ case Owner:
+ owner = fieldChange.value.toString();
+ break;
+ case Milestone:
+ milestone = fieldChange.value.toString();
+ break;
+ }
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("issue ");
+ sb.append(id.substring(0, 8));
+ sb.append(" (" + summary + ")\n");
+ for (Change change : changes) {
+ sb.append(change);
+ sb.append('\n');
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public int compareTo(IssueModel o) {
+ return o.created.compareTo(created);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof IssueModel)
+ return id.equals(((IssueModel) o).id);
+ return super.equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ public static class Change implements Serializable, Comparable<Change> {
+
+ private static final long serialVersionUID = 1L;
+
+ public final Date created;
+
+ public final String author;
+
+ public String id;
+
+ public char code;
+
+ public Comment comment;
+
+ public Set<FieldChange> fieldChanges;
+
+ public Set<Attachment> attachments;
+
+ public Change(String author) {
+ this.created = new Date((System.currentTimeMillis() / 1000) * 1000);
+ this.author = author;
+ this.id = StringUtils.getSHA1(created.toString() + author);
+ }
+
+ public boolean hasComment() {
+ return comment != null && !comment.deleted;
+ }
+
+ public void comment(String text) {
+ comment = new Comment(text);
+ comment.id = StringUtils.getSHA1(created.toString() + author + text);
+ }
+
+ public boolean hasAttachments() {
+ return !ArrayUtils.isEmpty(attachments);
+ }
+
+ public void addAttachment(Attachment attachment) {
+ if (attachments == null) {
+ attachments = new LinkedHashSet<Attachment>();
+ }
+ attachments.add(attachment);
+ }
+
+ public Attachment getAttachment(String name) {
+ for (Attachment attachment : attachments) {
+ if (attachment.name.equalsIgnoreCase(name)) {
+ return attachment;
+ }
+ }
+ return null;
+ }
+
+ public boolean hasField(Field field) {
+ return !StringUtils.isEmpty(getString(field));
+ }
+
+ public boolean hasFieldChanges() {
+ return !ArrayUtils.isEmpty(fieldChanges);
+ }
+
+ public Object getField(Field field) {
+ if (fieldChanges != null) {
+ for (FieldChange fieldChange : fieldChanges) {
+ if (fieldChange.field == field) {
+ return fieldChange.value;
+ }
+ }
+ }
+ return null;
+ }
+
+ public void setField(Field field, Object value) {
+ FieldChange fieldChange = new FieldChange(field, value);
+ if (fieldChanges == null) {
+ fieldChanges = new LinkedHashSet<FieldChange>();
+ }
+ fieldChanges.add(fieldChange);
+ }
+
+ public String getString(Field field) {
+ Object value = getField(field);
+ if (value == null) {
+ return null;
+ }
+ return value.toString();
+ }
+
+ @Override
+ public int compareTo(Change c) {
+ return created.compareTo(c.created);
+ }
+
+ @Override
+ public int hashCode() {
+ return id.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Change) {
+ return id.equals(((Change) o).id);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(new TimeUtils().timeAgo(created));
+ switch (code) {
+ case '+':
+ sb.append(" created by ");
+ break;
+ default:
+ if (hasComment()) {
+ sb.append(" commented on by ");
+ } else {
+ sb.append(" changed by ");
+ }
+ }
+ sb.append(author).append(" - ");
+ if (hasComment()) {
+ if (comment.deleted) {
+ sb.append("(deleted) ");
+ }
+ sb.append(comment.text).append(" ");
+ }
+ if (hasFieldChanges()) {
+ switch (code) {
+ case '+':
+ break;
+ default:
+ for (FieldChange fieldChange : fieldChanges) {
+ sb.append("\n ");
+ sb.append(fieldChange);
+ }
+ break;
+ }
+ }
+ return sb.toString();
+ }
+ }
+
+ public static class Comment implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public String text;
+
+ public String id;
+
+ public boolean deleted;
+
+ Comment(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public String toString() {
+ return text;
+ }
+ }
+
+ public static class FieldChange implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final Field field;
+
+ public final Object value;
+
+ FieldChange(Field field, Object value) {
+ this.field = field;
+ this.value = value;
+ }
+
+ @Override
+ public int hashCode() {
+ return field.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof FieldChange) {
+ return field.equals(((FieldChange) o).field);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return field + ": " + value;
+ }
+ }
+
+ public static class Attachment implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ public final String name;
+ public String id;
+ public long size;
+ public byte[] content;
+ public boolean deleted;
+
+ public Attachment(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof Attachment) {
+ return name.equalsIgnoreCase(((Attachment) o).name);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static enum Field {
+ Id, Summary, Description, Reporter, Owner, Type, Status, Priority, Milestone, Component, Labels;
+ }
+
+ public static enum Type {
+ Defect, Enhancement, Task, Review, Other;
+
+ public static Type fromObject(Object o) {
+ if (o instanceof Type) {
+ // cast and return
+ return (Type) o;
+ } else if (o instanceof String) {
+ // find by name
+ for (Type type : values()) {
+ String str = o.toString();
+ if (type.toString().equalsIgnoreCase(str)) {
+ return type;
+ }
+ }
+ } else if (o instanceof Number) {
+ // by ordinal
+ int id = ((Number) o).intValue();
+ if (id >= 0 && id < values().length) {
+ return values()[id];
+ }
+ }
+ return null;
+ }
+ }
+
+ public static enum Priority {
+ Low, Medium, High, Critical;
+
+ public static Priority fromObject(Object o) {
+ if (o instanceof Priority) {
+ // cast and return
+ return (Priority) o;
+ } else if (o instanceof String) {
+ // find by name
+ for (Priority priority : values()) {
+ String str = o.toString();
+ if (priority.toString().equalsIgnoreCase(str)) {
+ return priority;
+ }
+ }
+ } else if (o instanceof Number) {
+ // by ordinal
+ int id = ((Number) o).intValue();
+ if (id >= 0 && id < values().length) {
+ return values()[id];
+ }
+ }
+ return null;
+ }
+ }
+
+ public static enum Status {
+ New, Accepted, Started, Review, Queued, Testing, Done, Fixed, WontFix, Duplicate, Invalid;
+
+ public static Status fromObject(Object o) {
+ if (o instanceof Status) {
+ // cast and return
+ return (Status) o;
+ } else if (o instanceof String) {
+ // find by name
+ for (Status status : values()) {
+ String str = o.toString();
+ if (status.toString().equalsIgnoreCase(str)) {
+ return status;
+ }
+ }
+ } else if (o instanceof Number) {
+ // by ordinal
+ int id = ((Number) o).intValue();
+ if (id >= 0 && id < values().length) {
+ return values()[id];
+ }
+ }
+ return null;
+ }
+
+ public boolean atLeast(Status status) {
+ return ordinal() >= status.ordinal();
+ }
+
+ public boolean exceeds(Status status) {
+ return ordinal() > status.ordinal();
+ }
+
+ public boolean isClosed() {
+ return ordinal() >= Done.ordinal();
+ }
+
+ public Status next() {
+ switch (this) {
+ case New:
+ return Started;
+ case Accepted:
+ return Started;
+ case Started:
+ return Testing;
+ case Review:
+ return Testing;
+ case Queued:
+ return Testing;
+ case Testing:
+ return Done;
+ }
+ return Accepted;
+ }
+ }
+}
|