Java tutorial
/* * Copyright 2012 Mykolas and Anchialas. * * 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.kenai.redminenb.issue; import com.kenai.redminenb.Redmine; import com.kenai.redminenb.repository.RedmineRepository; import com.kenai.redminenb.util.ExceptionHandler; import com.kenai.redminenb.util.SafeAutoCloseable; import com.taskadapter.redmineapi.Include; import com.taskadapter.redmineapi.RedmineException; import com.taskadapter.redmineapi.bean.Attachment; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.util.Date; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import org.apache.commons.lang.StringUtils; import org.netbeans.modules.bugtracking.api.Issue; import org.netbeans.modules.bugtracking.spi.IssueController; import org.netbeans.modules.bugtracking.spi.IssueScheduleInfo; import org.netbeans.modules.bugtracking.spi.IssueStatusProvider; import org.openide.util.Mutex; import org.openide.util.NbBundle.Messages; /** * * @author Mykolas * @author Anchialas <anchialas@gmail.com> */ @Messages({ "# {0} - Tracker Name", "# {1} - Issue ID", "# {2} - Issue subject", "CTL_Issue={0} #{1}: {2}", "CTL_NewIssue=New Issue", // "CTL_Issue_Id=ID", "CTL_Issue_Id_Desc=Issue ID", "CTL_Issue_Project=Project", "CTL_Issue_Project_Desc=Project", "CTL_Issue_Tracker=Tracker", "CTL_Issue_Tracker_Desc=Issue Type", "CTL_Issue_ParentId=Parent task", "CTL_Issue_ParentId_Desc=Parent task", "CTL_Issue_StatusName=Status", "CTL_Issue_StatusName_Desc=Issue Status", "CTL_Issue_Category=Category", "CTL_Issue_Category_Desc=Issue Category", "CTL_Issue_PriorityText=Priority", "CTL_Issue_PriorityText_Desc=Issue Priority", "CTL_Issue_Subject=Subject", // Summary in Redmine "CTL_Issue_Subject_Desc=Issue Summary", "CTL_Issue_Author=Author", // Reporter in Redmine "CTL_Issue_Author_Desc=Issue Author", // Reporter in Redmine "CTL_Issue_Assignee=Assigned To", "CTL_Issue_Assignee_Desc=User to whom the issue is assigned", "CTL_Issue_CreatedOn=Created", "CTL_Issue_CreatedOn_Desc=creation time of the issue", "CTL_Issue_UpdatedOn=Updated", // Modification in Redmine "CTL_Issue_UpdatedOn_Desc=Last time the issue was modified", "CTL_Issue_TargetVersion=Target Version", "CTL_Issue_TargetVersion_Desc=Issue Target Version" }) public final class RedmineIssue { private static final Logger LOG = Logger.getLogger(RedmineIssue.class.getName()); static final String FIELD_ID = "id"; // NOI18N static final String FIELD_PROJECT = "project"; // NOI18N static final String FIELD_SUBJECT = "subject"; // NOI18N static final String FIELD_PARENT = "parentId"; // NOI18N static final String FIELD_ASSIGNEE = "assignee"; // NOI18N static final String FIELD_AUTHOR = "author"; // NOI18N static final String FIELD_PRIORITY_ID = "priorityId"; // NOI18N static final String FIELD_PRIORITY_TEXT = "priorityText"; // NOI18N static final String FIELD_DONERATIO = "doneRatio"; // NOI18N static final String FIELD_ESTIMATED_HOURS = "estimatedHours"; // NOI18N static final String FIELD_SPENT_HOURS = "spentHours"; // NOI18N static final String FIELD_START_DATE = "startDate"; // NOI18N static final String FIELD_DUE_DATE = "dueDate"; // NOI18N static final String FIELD_TRACKER = "tracker"; // NOI18N static final String FIELD_STATUS_ID = "statusId"; // NOI18N static final String FIELD_STATUS_NAME = "statusName"; // NOI18N static final String FIELD_DESCRIPTION = "description"; // NOI18N static final String FIELD_CREATED = "createdOn"; // NOI18N static final String FIELD_UPDATED = "updatedOn"; // NOI18N static final String FIELD_VERSION = "targetVersion"; // NOI18N static final String FIELD_CATEGORY = "category"; // NOI18N // static final DateFormat DATETIME_FORMAT = DateFormat.getDateTimeInstance(); private com.taskadapter.redmineapi.bean.Issue issue = new com.taskadapter.redmineapi.bean.Issue(); private RedmineRepository repository; private RedmineIssueController controller; private final PropertyChangeSupport support; private Object localSummary; private Object localDescription; public RedmineIssue(RedmineRepository repo) { repository = repo; support = new PropertyChangeSupport(this); } public RedmineIssue(RedmineRepository repo, String summary, String description) { repository = repo; support = new PropertyChangeSupport(this); issue.setSubject(summary); issue.setDescription(description); } public RedmineIssue(RedmineRepository repository, com.taskadapter.redmineapi.bean.Issue issue) { this(repository); setIssue(issue); } public void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener(listener); } private Integer busy = 0; private final SafeAutoCloseable busyHelper = new SafeAutoCloseable() { @Override public void close() { setBusy(false); } }; public SafeAutoCloseable busy() { setBusy(true); return busyHelper; } public synchronized boolean isBusy() { return busy != 0; } private synchronized void setBusy(boolean busyBool) { final boolean oldBusy = isBusy(); if (busyBool) { busy++; } else { busy--; } if (busy < 0) { throw new IllegalStateException("Inbalanced busy/nonbusy"); } Mutex.EVENT.writeAccess(new Runnable() { @Override public void run() { support.firePropertyChange("busy", oldBusy, busy != 0); } }); } public String getDisplayName() { return getDisplayName(issue); } public static String getDisplayName(com.taskadapter.redmineapi.bean.Issue issue) { if (issue == null) { return Bundle.CTL_NewIssue(); } return issue.getId() == null ? issue.getSubject() : Bundle.CTL_Issue(issue.getTracker().getName(), issue.getId(), issue.getSubject()); } public String getTooltip() { return getDisplayName(); } public String getID() { return isNew() ? null : String.valueOf(issue.getId()); } public String getSummary() { return isNew() ? Bundle.CTL_NewIssue() : issue.getSubject(); } public boolean isNew() { return issue == null || issue.getId() == null || issue.getId() == 0; } public boolean hasParent() { return issue != null && issue.getParentId() != null; } public boolean isFinished() { // TODO: improve this return "closed".equalsIgnoreCase(issue.getStatusName()); } void opened() { if (Redmine.LOG.isLoggable(Level.FINE)) { Redmine.LOG.log(Level.FINE, "issue {0} open start", new Object[] { getID() }); } String refresh = System.getProperty("org.netbeans.modules.bugzilla.noIssueRefresh"); // NOI18N if (refresh != null && refresh.equals("true")) { // NOI18N return; } if (SwingUtilities.isEventDispatchThread()) { new SwingWorker<Object, Object>() { @Override protected Object doInBackground() throws Exception { refresh(); return null; }; }.execute(); } else { refresh(); } repository.scheduleForRefresh(getID()); if (Redmine.LOG.isLoggable(Level.FINE)) { Redmine.LOG.log(Level.FINE, "issue {0} open finish", new Object[] { getID() }); } } void closed() { if (Redmine.LOG.isLoggable(Level.FINE)) { Redmine.LOG.log(Level.FINE, "issue {0} close start", new Object[] { getID() }); } repository.stopRefreshing(getID()); if (Redmine.LOG.isLoggable(Level.FINE)) { Redmine.LOG.log(Level.FINE, "issue {0} close finish", new Object[] { getID() }); } } public synchronized boolean refresh() { assert !SwingUtilities.isEventDispatchThread() : "Accessing remote host. Do not call in awt"; // NOI18N try { if (issue != null && issue.getId() != null) { setIssue(getRepository().getIssueManager().getIssueById(issue.getId(), Include.journals, Include.attachments, Include.watchers)); } return true; } catch (RedmineException | RuntimeException ex) { ExceptionHandler.handleException(LOG, "Can't refresh Redmine issue", ex); } return false; } public void addComment(String comment, boolean resolve) { Integer oldStatusId = issue.getStatusId(); try { issue.setNotes(comment); if (resolve) { // TODO This works for default Redmine Settings only. Add resolved status ID configuration to Redmine Option. issue.setStatusId(3); } getRepository().getIssueManager().update(issue); return; } catch (RedmineException | RuntimeException ex) { ExceptionHandler.handleException(LOG, "Can't add comment for a Redmine issue", ex); } issue.setStatusId(oldStatusId); } public void attachFile(File file, String description, String comment, boolean patch) { try { Attachment a = getRepository().getAttachmentManager().uploadAttachment("application/octed-stream", file); a.setDescription(description); issue.addAttachment(a); if (!StringUtils.isBlank(comment)) { issue.setNotes(comment); } getRepository().getIssueManager().update(issue); } catch (RedmineException | IOException ex) { ExceptionHandler.handleException(LOG, "Can't attach file to a Redmine issue", ex); } } public IssueController getController() { if (controller == null) { controller = new RedmineIssueController(this); } return controller; } public com.taskadapter.redmineapi.bean.Issue getIssue() { return issue; } public void setIssue(com.taskadapter.redmineapi.bean.Issue issue) { this.issue = issue; support.firePropertyChange(Issue.EVENT_ISSUE_DATA_CHANGED, null, null); } public RedmineRepository getRepository() { return repository; } @Override public String toString() { return getTooltip(); } public IssueStatusProvider.Status getStatus() { if (issue.getId() == null) { return IssueStatusProvider.Status.OUTGOING_NEW; } if (localDescription != null || localSummary != null) { return IssueStatusProvider.Status.OUTGOING_MODIFIED; } return IssueStatusProvider.Status.SEEN; } Date getDueDate() { if (issue != null) { return issue.getDueDate(); } else { return null; } } IssueScheduleInfo getSchedule() { if (issue != null && issue.getStartDate() != null) { return new IssueScheduleInfo(issue.getStartDate()); } else { return null; } } void setSchedule(IssueScheduleInfo scheduleInfo) { if (issue == null) { return; // Silently igonre setSchedule on not yet saved issues } if (scheduleInfo == null) { issue.setStartDate(null); } else { issue.setStartDate(scheduleInfo.getDate()); } getRepository().getRequestProcessor().execute(issueUpdate); } private Runnable issueUpdate = new Runnable() { @Override public void run() { try { getRepository().getIssueManager().update(issue); } catch (RedmineException | RuntimeException ex) { ExceptionHandler.handleException(LOG, "Failed to update start date for issue", ex); } } }; }