/src/main/java/com/osf/jira/plugins/util/TextUtil.java
Java | 417 lines | 288 code | 30 blank | 99 comment | 89 complexity | d35cfac59165660654d5ae320f4c99cd MD5 | raw file
- /**
- * Copyright (c) 2008, Outsourcing Factory Inc.,
- * http://www.outsourcing-factory.com/
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * * Neither the name of the Outsourcing Factory Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from this
- * software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- */
- package com.osf.jira.plugins.util;
-
- import java.text.NumberFormat;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.ResourceBundle;
- import java.util.Set;
-
- import org.apache.log4j.Logger;
- import org.apache.velocity.exception.VelocityException;
-
- import com.atlassian.core.util.DateUtils;
- import com.atlassian.jira.component.ComponentAccessor;
- import com.atlassian.jira.config.properties.ApplicationProperties;
- import com.atlassian.jira.datetime.DateTimeFormatter;
- import com.atlassian.jira.issue.CustomFieldManager;
- import com.atlassian.jira.issue.Issue;
- import com.atlassian.jira.issue.fields.AffectedVersionsSystemField;
- import com.atlassian.jira.issue.fields.AssigneeSystemField;
- import com.atlassian.jira.issue.fields.ComponentsSystemField;
- import com.atlassian.jira.issue.fields.CustomField;
- import com.atlassian.jira.issue.fields.Field;
- import com.atlassian.jira.issue.fields.FieldManager;
- import com.atlassian.jira.issue.fields.FixVersionsSystemField;
- import com.atlassian.jira.issue.fields.IssueLinksSystemField;
- import com.atlassian.jira.issue.fields.IssueTypeSystemField;
- import com.atlassian.jira.issue.fields.LabelsSystemField;
- import com.atlassian.jira.issue.fields.OriginalEstimateSystemField;
- import com.atlassian.jira.issue.fields.PrioritySystemField;
- import com.atlassian.jira.issue.fields.ProjectSystemField;
- import com.atlassian.jira.issue.fields.ResolutionSystemField;
- import com.atlassian.jira.issue.fields.SecurityLevelSystemField;
- import com.atlassian.jira.issue.fields.StatusSystemField;
- import com.atlassian.jira.issue.fields.TimeEstimateSystemField;
- import com.atlassian.jira.issue.fields.TimeSpentSystemField;
- import com.atlassian.jira.issue.fields.TimeTrackingSystemField;
- import com.atlassian.jira.issue.fields.layout.field.FieldLayoutItem;
- import com.atlassian.jira.issue.fields.layout.field.FieldLayoutManager;
- import com.atlassian.jira.issue.issuetype.IssueType;
- import com.atlassian.jira.issue.link.IssueLinkManager;
- import com.atlassian.jira.issue.link.IssueLinkType;
- import com.atlassian.jira.issue.link.LinkCollection;
- import com.atlassian.jira.ofbiz.OfBizValueWrapper;
- import com.atlassian.jira.security.JiraAuthenticationContext;
- import com.atlassian.jira.user.ApplicationUser;
- import com.atlassian.jira.util.I18nHelper;
- import com.atlassian.velocity.VelocityManager;
- import com.opensymphony.util.TextUtils;
-
- /**
- * @author Andriy Zhdanov
- */
- public class TextUtil {
- private static final Logger log = Logger.getLogger(TextUtil.class);
- private long secondsPerDay;
- private long secondsPerWeek;
- private ResourceBundle resourceBundle;
- private NumberFormat decimalFormat;
- private final VelocityManager velocityManager;
- private final FieldManager fieldManager;
- private final FieldLayoutManager fieldLayoutManager;
- private final CustomField epicLinkField;
-
- private static final String GH_PLUGIN_KEY = "com.pyxis.greenhopper.jira";
- private static final String EPIC_LINK_FIELD_ID = GH_PLUGIN_KEY + ":gh-epic-link";
-
- /**
- * Instantiate TextUtil object
- * @param i18nBean
- */
- public TextUtil(I18nHelper i18nBean) {
- ApplicationProperties ap = ComponentAccessor.getApplicationProperties();
- secondsPerDay = new Float(Float.valueOf(ap.getDefaultBackedString("jira.timetracking.hours.per.day")) * 3600).longValue();
- secondsPerWeek = new Float(Float.valueOf(ap.getDefaultBackedString("jira.timetracking.days.per.week")) * secondsPerDay).longValue();
- resourceBundle = i18nBean.getDefaultResourceBundle();
- decimalFormat = NumberFormat.getInstance(i18nBean.getLocale());
- velocityManager = ComponentAccessor.getVelocityManager();
- fieldManager = ComponentAccessor.getFieldManager();
- fieldLayoutManager = ComponentAccessor.getFieldLayoutManager();
- CustomFieldManager customFieldManager = ComponentAccessor.getCustomFieldManager();
- List<CustomField> customFields = customFieldManager.getCustomFieldObjects();
- CustomField epicLinkField = null;
- for (CustomField customField : customFields) {
- if (EPIC_LINK_FIELD_ID.equals(customField.getCustomFieldType().getKey())) {
- epicLinkField = customField;
- break;
- }
- }
- this.epicLinkField = epicLinkField;
- }
-
-
- /**
- * Format duration value
- * @param value
- * @return pretty formatted value, using Jira settings for hours in day, and days in week.
- */
- public String getPrettyDuration(long value)
- {
- return DateUtils.getDurationPrettySeconds(value, secondsPerDay, secondsPerWeek, resourceBundle);
- }
-
- /**
- * Format duration value in hours
- * @param value
- * @return value
- */
- public String getPrettyHours(long value)
- {
- return getHours(value) + "h";
- }
-
- /**
- * Format duration value in hours
- * @param value
- * @return pretty formatted value
- */
- public String getHours(long value)
- {
- return decimalFormat.format(((float)value) / 60 / 60);
- }
-
- /**
- * Get issue field value by field id for the issue.
- *
- * @param groupByFieldID
- * @param issue
- * @param dateTimeFormatter
- * @param req if to render html for timetracking field
- * @return String value concatenated for multi-select or null.
- */
- public String getFieldValue(String groupByFieldID, Issue issue,
- DateTimeFormatter dateTimeFormatter, String contextPath) {
- if (groupByFieldID == null) {
- return "";
- }
-
- Field groupByField = fieldManager.getField(groupByFieldID);
- if (groupByField == null) {
- log.error("Field '" + groupByFieldID + "' does not exist");
- return "";
- }
-
- // Set field value
- String fieldValue = null;
- if (groupByField instanceof CustomField) {
- CustomField customField = (CustomField) groupByField;
- IssueType issueType = issue.getIssueTypeObject();
- // Issue#439: Epic fields are not applied to sub-tasks
- if (customField.getCustomFieldType().getKey().startsWith(GH_PLUGIN_KEY)) {
- issue = getEpic(issue, customField);
- }
- Object value = customField.getValue(issue);
- if (value != null) {
- FieldLayoutItem fieldLayoutItem = fieldLayoutManager
- .getFieldLayout(issue.getProjectObject(), issueType.getId())
- .getFieldLayoutItem(groupByFieldID);
- if (fieldLayoutItem != null) {
- fieldValue = customField.getViewHtml(fieldLayoutItem, null, issue);
- } else {
- fieldValue = TextUtils.plainTextToHtml(fieldValue);
- }
-
- /*
- if (groupByField instanceof CustomFieldStattable) {
- StatisticsMapper sm = ((CustomFieldStattable)
- groupByField).getStatisticsMapper((CustomField) groupByField);
- fieldValue = sm.getValueFromLuceneField(value.toString()).toString();
- }
- if (value instanceof List) {
- fieldValue = getMultiValue((List) value);
- } else if (value instanceof Date ) {
- fieldValue = dateTimeFormatter.format((Date) value);
- } else if (value instanceof User){
- fieldValue = ((User) value).getDisplayName();
- } else if (value instanceof Map) {
- fieldValue = getMultiValue((Map) value);
- } else {
- fieldValue = value.toString();
- }
- */
- }
- } else if (groupByField instanceof ComponentsSystemField) {
- /*
- * Implementation to handle GroupBy Component. Issue TIME-54.
- * Caveat: When there are multiple components assigned to one
- * issue, the component names are concatenated and the grouping
- * is done by the concatenated string. The issue isn't
- * counted/grouped for each component.
- */
- fieldValue = getMultiValue(issue.getComponents());
- } else if (groupByField instanceof AffectedVersionsSystemField) {
- fieldValue = getMultiValue(issue.getAffectedVersions());
- } else if (groupByField instanceof FixVersionsSystemField) {
- fieldValue = getMultiValue(issue.getFixVersions());
- } else if (groupByField instanceof IssueTypeSystemField) {
- fieldValue = issue.getIssueTypeObject().getNameTranslation();
- } else if (groupByField instanceof LabelsSystemField) {
- fieldValue = getMultiValue(issue.getLabels());
- } else if (groupByField instanceof StatusSystemField) {
- fieldValue = issue.getStatusObject().getName();
- } else if (groupByField instanceof IssueLinksSystemField) {
- fieldValue = getIssueLinks(issue);
- } else if (groupByField instanceof SecurityLevelSystemField) {
- fieldValue = issue.getSecurityLevel().getString("name");
- } else if (groupByField instanceof TimeTrackingSystemField) {
- Long timeSpent = getIssueTimeSpent(issue);
- Long remaining = getIssueRemainingEstimate(issue);
- if (contextPath != null) {
- Map<String, Object> params = new HashMap<String, Object>();
- params.put("timeSpent", getFieldName("timespent") + " - " + getPrettyHours(timeSpent));
- params.put("timeSpentRate", Math.round(timeSpent) * 100 / (timeSpent + remaining));
- params.put("remaining", getFieldName("timeestimate") + " - " + getPrettyHours(remaining));
- params.put("remainingRate", Math.round(remaining) * 100 / (timeSpent + remaining));
- params.put("contextPath", contextPath);
- try {
- fieldValue = velocityManager.getBody("/templates/timesheetreport/", "timetracking.vm", params);
- } catch (VelocityException e) {
- log.warn("Error while rendering timetracking field", e);
- }
- } else {
- fieldValue = getPrettyHours(timeSpent) +
- "/" + getPrettyHours(remaining);
- }
- } else if (groupByField instanceof TimeSpentSystemField) {
- long timeSpent = getIssueTimeSpent(issue);
- fieldValue = getPrettyHours(timeSpent);
-
- } else if (groupByField instanceof TimeEstimateSystemField) {
- long remaining = getIssueRemainingEstimate(issue);
- fieldValue = getPrettyHours(remaining);
- } else if (groupByField instanceof OriginalEstimateSystemField && issue.getOriginalEstimate() != null) {
- long original = getIssueOriginalEstimate(issue);
- fieldValue = getPrettyHours(original);
- } else if (groupByField instanceof ProjectSystemField) {
- fieldValue = issue.getProjectObject().getName();
- } else if (groupByField instanceof PrioritySystemField) {
- fieldValue = issue.getPriorityObject().getName();
- } else if (groupByField instanceof ResolutionSystemField && issue.getResolutionObject() != null) {
- fieldValue = issue.getResolutionObject().getName();
- } else if (groupByField instanceof AssigneeSystemField) {
- ApplicationUser assignee = issue.getAssignee();
- if (assignee != null) {
- fieldValue = assignee.getDisplayName();
- } // else "NoValueForFieldOnIssue";
- } else {
- // TODO Couldn't find an easy way to get each fields value as
- // string. Workaround.
- try {
- fieldValue = issue.getString(groupByFieldID);
- } catch (RuntimeException e) {
- fieldValue = "FieldTypeValueNotApplicableForGrouping";
- }
- }
-
- // need a string as reference element in map for grouping
- if (fieldValue == null || fieldValue.trim().length() == 0) {
- fieldValue = "NoValueForFieldOnIssue";
- }
-
- return fieldValue;
- }
-
- private Issue getEpic(Issue issue, CustomField customField) {
- if (issue.isSubTask()) {
- return getEpic(issue.getParentObject(), customField);
- } else if (!EPIC_LINK_FIELD_ID.equals(customField.getCustomFieldType().getKey()) &&
- !isEpic(issue)) {
- if (epicLinkField != null) {
- Issue epic = (Issue) issue.getCustomFieldValue(epicLinkField);
- if (epic != null) {
- return epic;
- }
- }
- }
- return issue;
- }
-
- public CustomField getEpicLinkField() {
- return epicLinkField;
- }
-
- private boolean isEpic(Issue issue) {
- return "Epic".equals(issue.getIssueTypeObject().getName());
-
- }
-
- private long getIssueTimeSpent(Issue issue) {
- Long time = issue.getTimeSpent();
- if (time == null) {
- time = 0L;
- }
- return time;
- }
-
- private long getIssueRemainingEstimate(Issue issue) {
- Long time = issue.getEstimate();
- if (time == null) {
- time = 0L;
- }
- return time;
- }
-
- private long getIssueOriginalEstimate(Issue issue) {
- Long time = issue.getOriginalEstimate();
- if (time == null) {
- time = 0L;
- }
- return time;
- }
-
- private static String getIssueLinks(Issue issue) {
- StringBuffer result = new StringBuffer();
- IssueLinkManager issueLinkManager = ComponentAccessor.getIssueLinkManager();
- JiraAuthenticationContext authenticationContext = ComponentAccessor.getJiraAuthenticationContext();
- ApplicationUser remoteUser = authenticationContext.getLoggedInUser();
- LinkCollection linkCollection = issueLinkManager.getLinkCollection(issue, remoteUser);
- Set<IssueLinkType> linkTypes = linkCollection.getLinkTypes();
- for (IssueLinkType linkType : linkTypes) {
- appendLinkedIssues(linkCollection.getOutwardIssues(linkType.getName()), linkType.getOutward(), result);
- appendLinkedIssues(linkCollection.getInwardIssues(linkType.getName()), linkType.getInward(), result);
- }
- return result.toString();
- }
-
- private static void appendLinkedIssues(List<Issue> linkedIssues, String linkName, StringBuffer result) {
- if (linkedIssues != null) {
- result.append(TextUtils.plainTextToHtml(linkName));
- result.append(": ");
- for (int i = 0; i < linkedIssues.size(); i++) {
- Issue linkedIssue = linkedIssues.get(i);
- result.append(linkedIssue.getKey());
- if (i + 1 < linkedIssues.size()) {
- result.append(", ");
- }
- }
- result.append("<br/>");
- }
- }
-
- private static String getMultiValue(Collection<? extends Object> values) {
- StringBuffer fieldValue = new StringBuffer();
- for (Iterator<? extends Object> i = values.iterator(); i.hasNext();) {
- Object o = i.next();
- String value = null;
- if (o instanceof Map) {
- Map<String, Object> map= (Map<String, Object>) o;
- // do not check if (map.containsKey("name")) intentionally
- // for better diagnosability
- value = (String) map.get("name");
- } else if (o instanceof OfBizValueWrapper) {
- OfBizValueWrapper map= (OfBizValueWrapper) o;
- value = map.getString("name");
- } else if (o instanceof ApplicationUser){
- value = ((ApplicationUser) o).getDisplayName();
- } else if (o != null) {
- value = o.toString();
- }
- if (value != null) {
- fieldValue.append(value);
- fieldValue.append(", ");
- }
- }
- return fieldValue.length() > 0 ? fieldValue.substring(0, fieldValue.length() - 2) : "";
- }
-
- /**
- * @deprecated since JIRA 6 it's not needed
- */
- @Deprecated
- public static String getUnquotedString(String s) {
- return s; // JRA-31905: no need to escape anything anymore
- }
-
- public static String getFieldName(String fieldID) {
- FieldManager fieldManager = ComponentAccessor.getFieldManager();
- Field groupByField = fieldManager.getField(fieldID);
- return groupByField != null ? groupByField.getName() : "N/A";
- }
-
- }