James Moger
2012-06-18 67d4f89b0cddb3de05c20e08c20f1bea714c2a9e
src/com/gitblit/models/IssueModel.java
@@ -18,10 +18,13 @@
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.
@@ -31,7 +34,7 @@
 */
public class IssueModel implements Serializable, Comparable<IssueModel> {
   private static final long serialVersionUID = 1L;;
   private static final long serialVersionUID = 1L;
   public String id;
@@ -56,7 +59,8 @@
   public List<Change> changes;
   public IssueModel() {
      created = new Date();
      // the first applied change set the date appropriately
      created = new Date(0);
      type = Type.Defect;
      status = Status.New;
@@ -72,25 +76,22 @@
      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.hasFieldChanges()) {
            FieldChange field = change.getField(Field.Labels);
            if (field != null) {
               labels = field.value.toString();
            }
         if (change.hasField(Field.Labels)) {
            labels = change.getString(Field.Labels);
         }
      }
      if (!StringUtils.isEmpty(labels)) {
         list.addAll(StringUtils.getStringsFromValue(labels, " "));
      }
      return list;
   }
   public boolean hasLabel(String label) {
      return getLabels().contains(label);
   }
   public Attachment getAttachment(String name) {
@@ -106,16 +107,69 @@
      return attachment;
   }
   public void addChange(Change change) {
      if (changes == null) {
         changes = new ArrayList<Change>();
   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() {
      return summary;
      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
@@ -135,68 +189,46 @@
      return id.hashCode();
   }
   public static class Change implements Serializable {
   public static class Change implements Serializable, Comparable<Change> {
      private static final long serialVersionUID = 1L;
      public Date created;
      public final Date created;
      public String author;
      public final String author;
      public String id;
      public char code;
      public Comment comment;
      public List<FieldChange> fieldChanges;
      public Set<FieldChange> fieldChanges;
      public List<Attachment> attachments;
      public Set<Attachment> attachments;
      public void comment(String text) {
         comment = new Comment(text);
      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;
         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 boolean hasFieldChanges() {
         return !ArrayUtils.isEmpty(fieldChanges);
      }
      public FieldChange getField(Field field) {
         if (fieldChanges != null) {
            for (FieldChange fieldChange : fieldChanges) {
               if (fieldChange.field == field) {
                  return fieldChange;
               }
            }
         }
         return null;
      }
      public void setField(Field field, Object value) {
         FieldChange fieldChange = new FieldChange();
         fieldChange.field = field;
         fieldChange.value = value;
         if (fieldChanges == null) {
            fieldChanges = new ArrayList<FieldChange>();
         }
         fieldChanges.add(fieldChange);
      }
      public String getString(Field field) {
         FieldChange fieldChange = getField(field);
         if (fieldChange == null) {
            return null;
         }
         return fieldChange.value.toString();
      }
      public void addAttachment(Attachment attachment) {
         if (attachments == null) {
            attachments = new ArrayList<Attachment>();
            attachments = new LinkedHashSet<Attachment>();
         }
         attachments.add(attachment);
      }
@@ -210,9 +242,94 @@
         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() {
         return created.toString() + " by " + author;
         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();
      }
   }
@@ -221,6 +338,9 @@
      private static final long serialVersionUID = 1L;
      public String text;
      public String id;
      public boolean deleted;
      Comment(String text) {
@@ -237,9 +357,27 @@
      private static final long serialVersionUID = 1L;
      public Field field;
      public final Field field;
      public Object value;
      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() {
@@ -251,7 +389,8 @@
      private static final long serialVersionUID = 1L;
      public String name;
      public final String name;
      public String id;
      public long size;
      public byte[] content;
      public boolean deleted;
@@ -261,25 +400,104 @@
      }
      @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 {
      Summary, Description, Reporter, Owner, Type, Status, Priority, Milestone, Labels;
      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();
@@ -289,6 +507,10 @@
         return ordinal() > status.ordinal();
      }
      public boolean isClosed() {
         return ordinal() >= Done.ordinal();
      }
      public Status next() {
         switch (this) {
         case New: