PageRenderTime 27ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/main/java/com/atlassian/jconnect/jira/tabpanel/AbstractChartFragment.java

https://bitbucket.org/atlassian/jiraconnect-jiraplugin/
Java | 213 lines | 161 code | 30 blank | 22 comment | 12 complexity | 4e0bb9e354b4eb4665e5dbb87e84110f MD5 | raw file
  1. package com.atlassian.jconnect.jira.tabpanel;
  2. import com.atlassian.jconnect.jira.IssueHelper;
  3. import com.atlassian.jira.bc.issue.search.SearchService;
  4. import com.atlassian.jira.charts.jfreechart.ChartHelper;
  5. import com.atlassian.jira.config.properties.ApplicationProperties;
  6. import com.atlassian.jira.issue.Issue;
  7. import com.atlassian.jira.issue.search.SearchException;
  8. import com.atlassian.jira.issue.search.SearchResults;
  9. import com.atlassian.jira.plugin.projectpanel.fragment.impl.AbstractFragment;
  10. import com.atlassian.jira.project.browse.BrowseContext;
  11. import com.atlassian.jira.security.JiraAuthenticationContext;
  12. import com.atlassian.jira.util.ErrorCollection;
  13. import com.atlassian.jira.util.SimpleErrorCollection;
  14. import com.atlassian.jira.web.bean.PagerFilter;
  15. import com.atlassian.velocity.VelocityManager;
  16. import org.apache.commons.lang.StringUtils;
  17. import org.jfree.chart.ChartFactory;
  18. import org.jfree.chart.JFreeChart;
  19. import org.jfree.chart.axis.NumberAxis;
  20. import org.jfree.chart.plot.PlotOrientation;
  21. import org.jfree.chart.renderer.category.BarRenderer;
  22. import org.jfree.data.category.CategoryDataset;
  23. import org.jfree.data.category.DefaultCategoryDataset;
  24. import java.awt.*;
  25. import java.util.Collections;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.regex.Matcher;
  29. /**
  30. * Base class for generating issue breakdown charts by data from environment field.
  31. *
  32. */
  33. public abstract class AbstractChartFragment extends AbstractFragment {
  34. protected static final int WIDTH = com.atlassian.jira.charts.ChartFactory.FRAGMENT_IMAGE_WIDTH;
  35. protected static final int HEIGHT = com.atlassian.jira.charts.ChartFactory.FRAGMENT_IMAGE_HEIGHT;
  36. private static final Color NICE_BLUE = new Color(51 , 102, 204);
  37. private static final Color NICE_RED = new Color(220, 57, 18);
  38. private static final Color NICE_YELLOW = new Color(255, 153, 0);
  39. private static final Color NICE_GREEN = new Color(16, 150, 24);
  40. private static final Color NICE_VIOLET = new Color(153, 0, 153);
  41. private static final Color[] BAR_COLORS = {
  42. NICE_BLUE,
  43. NICE_RED,
  44. NICE_YELLOW,
  45. NICE_GREEN,
  46. NICE_VIOLET
  47. };
  48. protected final SearchService searchService;
  49. public AbstractChartFragment(SearchService searchService, VelocityManager velocityManager,
  50. ApplicationProperties applicationProperites,
  51. JiraAuthenticationContext jiraAuthenticationContext) {
  52. super(velocityManager, applicationProperites, jiraAuthenticationContext);
  53. this.searchService = searchService;
  54. }
  55. // final is quite strict but as of now we don't see any modification you would apply here, and if somebody overrides
  56. // this template method, half of this class is rendered useless so you may as well not extend it at all!
  57. // i.e. think about how to hook into this instead of overriding!
  58. @Override
  59. protected final Map<String, Object> createVelocityParams(BrowseContext ctx) {
  60. final Map<String, Object> params = super.createVelocityParams(ctx);
  61. final ErrorCollection errors = new SimpleErrorCollection();
  62. final List<Issue> issues = collectIssues(ctx, errors);
  63. generateChart(params, issues, errors);
  64. params.put("errors", errors);
  65. return params;
  66. }
  67. private List<Issue> collectIssues(BrowseContext ctx, ErrorCollection errors) {
  68. final String jql = "project=" + ctx.getProject().getKey() + " order by createdDate";
  69. SearchService.ParseResult parseResult = searchService.parseQuery(jiraAuthenticationContext.getLoggedInUser(), jql);
  70. if (!parseResult.isValid()) {
  71. errors.addErrorMessages(parseResult.getErrors().getErrorMessages());
  72. } else {
  73. try {
  74. final SearchResults results = searchService.search(jiraAuthenticationContext.getLoggedInUser(),
  75. parseResult.getQuery(), PagerFilter.newPageAlignedFilter(0, 500));
  76. return results.getIssues();
  77. } catch (SearchException se) {
  78. errors.addErrorMessage(se.getLocalizedMessage());
  79. }
  80. }
  81. return Collections.emptyList();
  82. }
  83. protected CategoryDataset createDataset(List<Issue> issues) {
  84. final DefaultCategoryDataset answer = new DefaultCategoryDataset();
  85. for (Issue issue : issues) {
  86. String fieldValue = parseEnvironmentString(issue.getEnvironment());
  87. if (fieldValue == null) {
  88. continue; // ignore...
  89. }
  90. fieldValue = fieldValue.trim();
  91. if (!answer.getColumnKeys().contains(fieldValue)) {
  92. answer.addValue(0, issueCountName(), fieldValue);
  93. }
  94. answer.incrementValue(1, issueCountName(), fieldValue);
  95. }
  96. return answer;
  97. }
  98. protected void generateChart(Map<String, Object> params, List<Issue> issues, ErrorCollection errors) {
  99. if (issues.isEmpty()) {
  100. return;
  101. }
  102. try {
  103. final CategoryDataset dataset = createDataset(issues);
  104. JFreeChart chart = ChartFactory.createBarChart(
  105. null,
  106. getText(i18nPrefix() + ".xaxistitle"),
  107. issueCountName(),
  108. dataset,
  109. PlotOrientation.VERTICAL,
  110. false,
  111. true,
  112. false
  113. );
  114. chart.getCategoryPlot().setRenderer(new CustomBarColorsRenderer(BAR_COLORS));
  115. chart.setBackgroundPaint(Color.white);
  116. chart.getCategoryPlot().getRangeAxis().setStandardTickUnits(NumberAxis.createIntegerTickUnits());
  117. ChartHelper chartHelper = new ChartHelper(chart);
  118. chartHelper.generate(WIDTH, HEIGHT);
  119. params.put("chart", chartHelper.getLocation());
  120. params.put("imagemap", chartHelper.getImageMap());
  121. params.put("imagemapName", chartHelper.getImageMapName());
  122. params.put("imageWidth", WIDTH);
  123. params.put("imageHeight", HEIGHT);
  124. } catch (Exception e) {
  125. log.error("Error while creating chart", e);
  126. errors.addErrorMessage(jiraAuthenticationContext.getI18nHelper().getText(
  127. i18nPrefix() + ".charterror", e.getMessage()));
  128. }
  129. }
  130. /**
  131. * Matcher group number for regex-parsing the environment field.
  132. *
  133. * @return matcher group number
  134. * @see IssueHelper#ENV_FIELD_PATTERN
  135. */
  136. public abstract int groupNumber();
  137. /**
  138. * I18n prefix to get standard text resources.
  139. *
  140. * @return i18n prefix
  141. */
  142. public String i18nPrefix() {
  143. return JiraConnectProjectTabPanel.I18N_PREFIX + "." + getId();
  144. }
  145. protected String parseEnvironmentString(String environment) {
  146. if (environment == null || StringUtils.isEmpty(environment)) {
  147. return null;
  148. }
  149. Matcher matcher = IssueHelper.ENV_FIELD_PATTERN.matcher(environment);
  150. if (matcher.find()) {
  151. return matcher.group(groupNumber());
  152. } else {
  153. return getText(i18nPrefix() + ".unknown");
  154. }
  155. }
  156. protected final String getText(String key) {
  157. return jiraAuthenticationContext.getI18nHelper().getText(key);
  158. }
  159. public final String issueCountName() {
  160. return getText(i18nPrefix() + ".yaxistitle");
  161. }
  162. @Override
  163. protected final String getTemplateDirectoryPath() {
  164. return JiraConnectProjectTabPanel.TEMPLATE_DIR;
  165. }
  166. public boolean showFragment(BrowseContext browseContext) {
  167. // show by default
  168. return true;
  169. }
  170. /**
  171. * A custom renderer that returns a different color for each bar in a single series.
  172. */
  173. private static class CustomBarColorsRenderer extends BarRenderer {
  174. private final Paint[] colors;
  175. public CustomBarColorsRenderer(final Paint[] colors) {
  176. this.colors = colors;
  177. }
  178. @Override
  179. public Paint getItemPaint(final int row, final int column) {
  180. return this.colors[column % this.colors.length];
  181. }
  182. }
  183. }