From bd01eebfa57b4012bc7a7abc1aaaa1b69278b9de Mon Sep 17 00:00:00 2001 From: James Moger <james.moger@gitblit.com> Date: Sun, 19 Feb 2012 15:38:50 -0500 Subject: [PATCH] Merged issues/lucene branch --- src/com/gitblit/models/IssueModel.java | 532 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 532 insertions(+), 0 deletions(-) diff --git a/src/com/gitblit/models/IssueModel.java b/src/com/gitblit/models/IssueModel.java new file mode 100644 index 0000000..3c191e2 --- /dev/null +++ b/src/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(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; + } + } +} -- Gitblit v1.9.1