New file |
| | |
| | | /*
|
| | | * 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;
|
| | | }
|
| | | }
|
| | | }
|