Issue.java

/*
 * This software was designed and created by Jason Carroll.
 * Copyright (c) 2002, 2003, 2004 Jason Carroll.
 * The author can be reached at jcarroll@cowsultants.com
 * ITracker website: http://www.cowsultants.com
 * ITracker forums: http://www.cowsultants.com/phpBB/index.php
 *
 * This program is free software; you can redistribute it and/or modify
 * it only under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.itracker.model;

import org.apache.commons.lang.builder.CompareToBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * A project issue.
 * <p/>
 * <p>This class contains the core of the information we're managing. </p>
 *
 * @author ready
 */
public class Issue extends AbstractEntity implements Comparable<Entity> {

    /**
     *
     */
    private static final long serialVersionUID = 1L;
    public static final Comparator<Issue> STATUS_COMPARATOR =
            new StatusComparator();

    public static final Comparator<Issue> PROJECT_AND_STATUS_COMPARATOR =
            new ProjectAndStatusComparator();

    public static final Comparator<Issue> OWNER_AND_STATUS_COMPARATOR =
            new OwnerAndStatusComparator();

    public static final Comparator<Issue> SEVERITY_COMPARATOR =
            new SeverityComparator();

    private String description;

    private Integer severity;

    private Integer status;

    /* PENDING: consider using an int enumeration like severity and status. */
    private String resolution;

    private Project project;

    /**
     * The User who created this Issue.
     * <p/>
     * <p>Issue - User (creator) is a N-1 relationship. </p>
     */
    private User creator;

    /**
     * The User who owns this Issue.
     * <p/>
     * <p>This is the user who is responsible for the resolution of
     * this Issue. </p>
     * <p/>
     * <p>Issue - User (owner) is a N-1 relationship. </p>
     */
    private User owner;

    /**
     * Project version for which this issue must be fixed.
     * <p/>
     * <p>Issue - Version (targetVersion) is a N-1 relationship. </p>
     */
    private Version targetVersion;

    /**
     * List of project components affected by this Issue.
     * <p/>
     * <p>An Issue can be associated with 1 or more Components (Issue - Component
     * is a M-N relationship). </p>
     */
    private List<Component> components = new ArrayList<Component>();

    /**
     * List of project versions affected by this Issue.
     * <p/>
     * <p>Issue - Version (version) is a M-N relationship. </p>
     */
    private List<Version> versions = new ArrayList<Version>();

    /**
     * List of custom fields and values.
     * <p/>
     * <p>Issue - IssueField is a 1-N relationship. </p>
     */
    private List<IssueField> fields = new ArrayList<IssueField>();

    /**
     * List of files attached to this Issue.
     * <p/>
     * <p>Issue - IssueAttachment is a 1-N relationship. </p>
     */
    private List<IssueAttachment> attachments = new ArrayList<IssueAttachment>();

    /**
     * List of relations with other Issues.
     * <p/>
     * <p>Issue - IssueRelation is a 1-N relationship. </p>
     */
    private List<IssueRelation> relations = new ArrayList<IssueRelation>();

    /* PENDING: do we really need to navigate these relationships from an Issue ? 
    * Moving these as DAO methods would make an Issue more light-weight.
    */

    /**
     * Issue - Notification is 1-N relationship.
     * <p/>
     * <p>Does this association need to be navigatable in this direction
     * as it was in iTracker 2 ? </p>
     */
    private List<Notification> notifications = new ArrayList<Notification>();

    /**
     * Issue - IssueActivity is 1-N relationship.
     * <p/>
     * <p>Does this association need to be navigatable in this direction
     * as it was in iTracker 2 ? </p>
     */
    private List<IssueActivity> activities = new ArrayList<IssueActivity>();

    /**
     * Issue - IssueHistory is 1-N relationship.
     * <p/>
     * <p>Does this association need to be navigatable in this direction
     * as it was in iTracker 2 ? </p>
     */
    private List<IssueHistory> history = new ArrayList<IssueHistory>();


    /**
     * Default constructor (required by Hibernate).
     * <p/>
     * <p>PENDING: should be <code>private</code> so that it can only be used
     * by Hibernate, to ensure that the fields which form an instance's
     * identity are always initialized/never <tt>null</tt>. </p>
     */
    public Issue() {
    }

    public List<IssueActivity> getActivities() {
        return activities;
    }

    public void setActivities(List<IssueActivity> activities) {
        this.activities = activities;
    }

    public List<IssueAttachment> getAttachments() {
        return attachments;
    }

    public void setAttachments(List<IssueAttachment> attachments) {
        this.attachments = attachments;
    }

    public List<Component> getComponents() {
        return components;
    }

    public void setComponents(List<Component> components) {
        this.components = components;
    }

    public User getCreator() {
        return creator;
    }

    public void setCreator(User creator) {
        this.creator = creator;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<IssueField> getFields() {
        return fields;
    }

    public void setFields(List<IssueField> fields) {
        this.fields = fields;
    }

    public List<IssueHistory> getHistory() {
        return history;
    }

    public void setHistory(List<IssueHistory> history) {
        this.history = history;
    }

    public List<Notification> getNotifications() {
        return notifications;
    }

    public void setNotifications(List<Notification> notifications) {
        this.notifications = notifications;
    }

    public User getOwner() {
        return owner;
    }

    public void setOwner(User owner) {
        this.owner = owner;
    }

    public Project getProject() {
        return project;
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public List<IssueRelation> getRelations() {
        return relations;
    }

    public void setRelations(List<IssueRelation> relations) {
        this.relations = relations;
    }

    public String getResolution() {
        return resolution;
    }

    public void setResolution(String resolution) {
        this.resolution = resolution;
    }

    public Integer getSeverity() {
        return severity;
    }

    public void setSeverity(Integer severity) {
        this.severity = severity;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Version getTargetVersion() {
        return targetVersion;
    }

    public void setTargetVersion(Version targetVersion) {
        this.targetVersion = targetVersion;
    }

    public List<Version> getVersions() {
        return versions;
    }

    public void setVersions(List<Version> versions) {
        this.versions = versions;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("id", getId()).append("description", getDescription()).append("owner", getOwner()).append("severity", getSeverity()).append("status", getStatus()).append("targetVersion", getTargetVersion()).toString();
    }


    /**
     * Compares 2 Issues by status and severity.
     */
    private static class StatusComparator implements Comparator<Issue>, Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 1L;

        public int compare(Issue a, Issue b) {
            return new CompareToBuilder().append(a.getStatus(), b.getStatus()).append(a.getSeverity(), b.getSeverity()).append(a.getId(), b.getId()).toComparison();
        }

    }

    private static class ProjectAndStatusComparator implements Comparator<Issue>, Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 1L;

        public int compare(Issue a, Issue b) {
            return new CompareToBuilder().append(a.getProject(), b.getProject(), Project.PROJECT_COMPARATOR).append(a.getStatus(), b.getStatus()).append(a.getId(), b.getId()).toComparison();
        }

    }

    private static class OwnerAndStatusComparator implements Comparator<Issue>, Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 1L;

        public int compare(Issue a, Issue b) {

            return new CompareToBuilder().append(a.getOwner(), b.getOwner(), User.NAME_COMPARATOR).append(a.getStatus(), b.getStatus()).append(a.getId(), b.getId()).toComparison();

        }
    }

    private static class SeverityComparator implements Comparator<Issue>, Serializable {
        /**
         *
         */
        private static final long serialVersionUID = 1L;

        public int compare(Issue a, Issue b) {

            return new CompareToBuilder().append(a.getSeverity(), b.getSeverity()).append(a.getStatus(), b.getStatus()).append(a.getId(), b.getId()).toComparison();
        }

    }

}