CustomField.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.ToStringBuilder;
- import org.apache.log4j.Logger;
- import org.itracker.IssueException;
- import org.itracker.model.util.CustomFieldUtilities;
- import java.text.ParseException;
- import java.text.SimpleDateFormat;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Locale;
- import java.util.ResourceBundle;
- /**
- * A custom field that can be added to an Issue.
- * <p/>
- * <p>
- * Allows the user to dynamically extend the set of attributes/properties of the
- * Issue class.
- * </p>
- * <p/>
- * <p>
- * A CustomField must be configured to be used in a Project in order to extend
- * the attributes/properties of all Issues created for that project. A
- * CustomField may be used in more than 1 project. (Project - CustomField is a
- * M-N relathionship).
- * </p>
- * <p/>
- * <p>
- * A CustomField has a type, which indicates the data type of its value. <br>
- * The special type <code>LIST</code>, allows to associate a list of string
- * options to a CustomField, which are the enumeration of possible values for
- * that field. <br>
- * Each option value is represented by a CustomFieldValue instance. There's a
- * 1-N relationship between CustomField - CustomFieldValue. A CustomFieldValue
- * can only belong to 1 CustomField (composition).
- * </p>
- * <p/>
- * <p>
- * A value of a CustomField for a given Issue is represented by an IssueField
- * instance. (CustomField - IssueField is a 1-N relationship).
- * </p>
- *
- * @author ready
- * @see CustomFieldValue
- * @see IssueField
- */
- public class CustomField extends AbstractEntity implements Comparable<Entity> {
- private static final Logger logger = Logger.getLogger(CustomField.class);
- /**
- * Dateformat able to parse datepicker generated date string (dd/MM/yyyy)
- */
- public static final SimpleDateFormat DEFAULT_DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy");
- /**
- *
- */
- private static final long serialVersionUID = 1L;
- /**
- * Field value data type.
- */
- private Type type;
- /**
- * Display format to use if <code>fieldType</code> is a Date.
- * <p/>
- * TODO: use type-safe enum CustomField.DateFormat
- */
- private String dateFormat;
- /**
- * Whether this field is mandatory or optional. PENDING: this should be
- * specified when the field is used in a project!
- */
- private boolean required;
- /**
- * List of options for a field of type <code>LIST</code>.
- * <p/>
- * <p>
- * This is the enumeration of possible values for the field.
- * </p>
- * <p/>
- * Note: this field used to be named <code>values</code> is iTracker 2.
- * <p/>
- * <p>
- * PENDING: There's no way to use this as a list of proposed values,
- * allowing the user to enter a value that's not in this list.
- * </p>
- */
- private List<CustomFieldValue> options = new ArrayList<CustomFieldValue>();
- /**
- * Whether the options of a field of type List should be sorted by their
- * name rather than by {@link CustomFieldValue#getSortOrder() }.
- */
- private boolean sortOptionsByName;
- /**
- * 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 CustomField() {
- }
- public Type getFieldType() {
- return type;
- }
- public void setFieldType(Type type) {
- this.type = type;
- }
- public String getDateFormat() {
- return dateFormat;
- }
- public void setDateFormat(String dateFormat) {
- this.dateFormat = dateFormat;
- }
- public boolean isRequired() {
- return required;
- }
- public void setRequired(boolean required) {
- this.required = required;
- }
- public List<CustomFieldValue> getOptions() {
- return options;
- }
- public void setOptions(List<CustomFieldValue> options) {
- this.options = options;
- }
- /**
- * Adds a new option value/name to the custom field.
- * <p/>
- * <p>
- * New options are put at the end of the list even if they should be sorted.
- * <br>
- * This method is mainly used to build a new custom field so it can be saved
- * later.
- * </p>
- *
- * @param value the option value
- * @param label the label/name for the new option
- * @deprecated this can not be in the entity, replace by Utility or service.
- */
- public void addOption(String value, String label) {
- this.options.add(new CustomFieldValue(this, value));
- }
- public boolean isSortOptionsByName() {
- return sortOptionsByName;
- }
- public void setSortOptionsByName(boolean sortOptionsByName) {
- this.sortOptionsByName = sortOptionsByName;
- }
- @Override
- public String toString() {
- return new ToStringBuilder(this)
- .append("id", getId())
- .append("type", getFieldType())
- .append("sortOptionsByName", isSortOptionsByName()).toString();
- }
- /**
- * Checks if the given value is assignable to this custom field.
- *
- * @param value custom field data
- * @throws org.itracker.IssueException if it isn't
- * @see IssueField#setValue(String, Locale, ResourceBundle)
- */
- public void checkAssignable(String value, Locale locale,
- ResourceBundle bundle) throws IssueException {
- if (this.isRequired() && (value == null || value.trim().length() == 0)) {
- throw new IssueException("Value is required.", IssueException.TYPE_CF_REQ_FIELD);
- }
- switch (this.type) {
- case INTEGER:
- try {
- Integer.parseInt(value);
- } catch (NumberFormatException nfe) {
- throw new IssueException("Invalid integer.",
- IssueException.TYPE_CF_PARSE_NUM);
- }
- break;
- case DATE:
- if (!CustomFieldUtilities.DATE_FORMAT_UNKNOWN.equals(this.dateFormat)) {
- SimpleDateFormat format =
- // DEFAULT_DATE_FORMAT;
- new SimpleDateFormat(bundle
- .getString("itracker.dateformat." + this.dateFormat),
- locale);
- try {
- format.parse(value);
- } catch (ParseException ex) {
- throw new IssueException("Invalid date format.",
- IssueException.TYPE_CF_PARSE_DATE);
- }
- }
- break;
- case LIST:
- for (CustomFieldValue customFieldValue : getOptions()) {
- if (customFieldValue.getValue().equalsIgnoreCase(value)) {
- return;
- }
- }
- if (logger.isDebugEnabled()) {
- logger.debug("checkAssignable: could not assign value to custom field values: " + value + ", " + getOptions());
- }
- throw new IssueException("Invalid value.", IssueException.TYPE_CF_INVALID_LIST_OPTION);
- default:
- // Value is OK
- }
- }
- /**
- * Enumeration of possible data types.
- */
- public static enum Type implements IntCodeEnum<Type> {
- STRING(1), INTEGER(2), DATE(3), LIST(4);
- private final Integer code;
- private Type(Integer code) {
- this.code = code;
- }
- public Integer getCode() {
- return code;
- }
- public Type fromCode(Integer code) {
- return Type.valueOf(code);
- }
- public static Type valueOf(Integer code) {
- for (Type val: values()) {
- if (val.code.compareTo(code) == 0) {
- return val;
- }
- }
- throw new IllegalArgumentException("Unknown code : " + code);
- }
- }
- /**
- * Date format for fields of type DATE.
- * <p/>
- * PENDING: consider replacing the DATE Type with these 3 new data types.
- */
- public static enum DateFormat {
- DATE_TIME("full"), DATE("dateonly"), TIME("timeonly");
- final String code;
- DateFormat(String code) {
- this.code = code;
- }
- }
- }