package eu.doppel_helix.netbeans.mantisintegration.query;
import biz.futureware.mantisconnect.AccountData;
import biz.futureware.mantisconnect.FilterSearchData;
import biz.futureware.mantisconnect.IssueHeaderData;
import biz.futureware.mantisconnect.ObjectRef;
import eu.doppel_helix.netbeans.mantisintegration.issue.MantisIssue;
import eu.doppel_helix.netbeans.mantisintegration.repository.MantisRepository;
import eu.doppel_helix.netbeans.mantisintegration.util.SafeAutocloseable;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.math.BigInteger;
import java.rmi.RemoteException;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.xml.rpc.ServiceException;
import org.netbeans.modules.bugtracking.spi.QueryController;
import org.netbeans.modules.bugtracking.spi.QueryProvider;
public class MantisQuery {
public enum Combination {
ALL,
ANY
}
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private final MantisRepository mr;
private QueryProvider.IssueContainer<MantisIssue> issueContainer;
private MantisQueryController mqc;
private String id = UUID.randomUUID().toString();
private String name = null;
private BigInteger projectId;
private BigInteger serversideFilterId;
private AccountData reporter;
private AccountData assignedTo;
private String category;
private ObjectRef severity;
private ObjectRef resolution;
private ObjectRef status;
private ObjectRef priority;
private ObjectRef viewStatus;
private Date lastUpdateAfter;
private Date lastUpdateBefore;
private String summaryFilter;
private Combination combination = Combination.ALL;
private final Set<String> matchingIds = new HashSet<>();
private Integer busy = 0;
private boolean saved = false;
private final SafeAutocloseable busyHelper = new SafeAutocloseable() {
@Override
public void close() {
setBusy(false);
}
};
public SafeAutocloseable busy() {
setBusy(true);
return busyHelper;
}
public MantisQuery(MantisRepository mr) {
this.mr = mr;
}
MantisRepository getMantisRepository() {
return mr;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isSaved() {
return saved;
}
public void setSaved(boolean saved) {
this.saved = saved;
}
public void save() {
mr.saveQuery(this, true);
setSaved(true);
}
public void remove() {
matchingIds.clear();
mr.deleteQuery(id);
}
public Collection<MantisIssue> getIssues() throws ServiceException, RemoteException {
return mr.getIssues(true, matchingIds.toArray(new String[matchingIds.size()]));
}
/**
* DO NOT USE THIS! It is only here for serialization!
*
* @return
*/
public Set<String> getMatchingIds() {
return matchingIds;
}
public boolean contains(String id) {
return matchingIds.contains(id);
}
public void refresh() throws ServiceException, RemoteException {
try (SafeAutocloseable ac = busy()) {
if (issueContainer != null) {
issueContainer.refreshingStarted();
}
matchingIds.clear();
for (MantisIssue mi : mr.findIssues(this)) {
matchingIds.add(mi.getIdAsString());
}
if(isSaved()) {
mr.saveQuery(this, false);
}
// Assumption: this is called off the EDT and should do the heavy
// lifting, while getIssues is called on the EDT and needs to be
// lightweight
List<MantisIssue> mis = mr.getIssues(
false, matchingIds.toArray(new String[matchingIds.size()]));
if (issueContainer != null) {
issueContainer.clear();
issueContainer.add(mis.toArray(new MantisIssue[mis.size()]));
issueContainer.refreshingFinished();
}
}
}
public QueryController getController() {
if (mqc == null) {
mqc = new MantisQueryController(this);
}
return mqc;
}
private void firePropertyChanged(final String property, final Object oldValue, final Object newValue) {
pcs.firePropertyChange(property, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcs.removePropertyChangeListener(listener);
}
public synchronized boolean isBusy() {
return busy != 0;
}
private synchronized void setBusy(boolean busyBool) {
boolean oldBusy = isBusy();
if (busyBool) {
busy++;
} else {
busy--;
}
if (busy < 0) {
throw new IllegalStateException("Inbalanced busy/nonbusy");
}
firePropertyChanged("busy", oldBusy, isBusy());
}
public BigInteger getProjectId() {
return projectId;
}
public void setProjectId(BigInteger filterProjectId) {
BigInteger oldId = this.projectId;
this.projectId = filterProjectId;
pcs.firePropertyChange("projectId", oldId, this.projectId);
}
public BigInteger getServersideFilterId() {
return serversideFilterId;
}
public void setServersideFilterId(BigInteger filterId) {
BigInteger oldId = this.serversideFilterId;
this.serversideFilterId = filterId;
pcs.firePropertyChange("filterId", oldId, this.serversideFilterId);
}
public AccountData getReporter() {
return reporter;
}
public void setReporter(AccountData reporter) {
AccountData oldValue = this.reporter;
this.reporter = reporter;
pcs.firePropertyChange("reporter", oldValue, reporter);
}
public AccountData getAssignedTo() {
return assignedTo;
}
public void setAssignedTo(AccountData assignedTo) {
AccountData oldValue = this.assignedTo;
this.assignedTo = assignedTo;
pcs.firePropertyChange("assignedTo", oldValue, assignedTo);
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
String oldValue = this.category;
this.category = category;
pcs.firePropertyChange("category", oldValue, category);
}
public ObjectRef getSeverity() {
return severity;
}
public void setSeverity(ObjectRef severity) {
ObjectRef oldValue = this.severity;
this.severity = severity;
pcs.firePropertyChange("severity", oldValue, severity);
}
public ObjectRef getResolution() {
return resolution;
}
public void setResolution(ObjectRef resolution) {
ObjectRef oldValue = this.resolution;
this.resolution = resolution;
pcs.firePropertyChange("resolution", oldValue, resolution);
}
public ObjectRef getStatus() {
return status;
}
public void setStatus(ObjectRef status) {
ObjectRef oldValue = this.status;
this.status = status;
pcs.firePropertyChange("status", oldValue, status);
}
public ObjectRef getPriority() {
return priority;
}
public void setPriority(ObjectRef priority) {
ObjectRef oldValue = this.priority;
this.priority = priority;
pcs.firePropertyChange("priority", oldValue, priority);
}
public ObjectRef getViewStatus() {
return viewStatus;
}
public void setViewStatus(ObjectRef viewStatus) {
ObjectRef oldValue = this.viewStatus;
this.viewStatus = viewStatus;
pcs.firePropertyChange("viewStatus", oldValue, viewStatus);
}
public Date getLastUpdateAfter() {
return lastUpdateAfter;
}
public void setLastUpdateAfter(Date lastUpdateAfter) {
Date oldValue = this.lastUpdateAfter;
this.lastUpdateAfter = lastUpdateAfter;
pcs.firePropertyChange("lastUpdateAfter", oldValue, lastUpdateAfter);
}
public Date getLastUpdateBefore() {
return lastUpdateBefore;
}
public void setLastUpdateBefore(Date lastUpdateBefore) {
Date oldValue = this.lastUpdateBefore;
this.lastUpdateBefore = lastUpdateBefore;
pcs.firePropertyChange("lastUpdateBefore", oldValue, lastUpdateBefore);
}
public String getSummaryFilter() {
return summaryFilter;
}
public void setSummaryFilter(String summaryFilter) {
String oldValue = this.summaryFilter;
this.summaryFilter = summaryFilter;
pcs.firePropertyChange("summaryFilter", oldValue, summaryFilter);
}
public Combination getCombination() {
return combination;
}
public void setCombination(Combination combination) {
if(combination == null) {
combination = Combination.ALL;
}
Combination oldValue = this.combination;
this.combination = combination;
pcs.firePropertyChange("combination", oldValue, combination);
}
public QueryProvider.IssueContainer<MantisIssue> getIssueContainer() {
return issueContainer;
}
public void setIssueContainer(QueryProvider.IssueContainer<MantisIssue> issueContainer) {
this.issueContainer = issueContainer;
}
public boolean matchesFilter(IssueHeaderData id) {
int matches = 0;
int checks = 0;
if(getReporter() != null) {
checks++;
if(getReporter().getId().equals(id.getReporter())) {
matches++;
}
}
if(getAssignedTo() != null) {
checks++;
if(id.getHandler() != null && getAssignedTo().getId().equals(id.getHandler())) {
matches++;
}
}
if(getCategory() != null) {
checks++;
if(getCategory().equals(id.getCategory())) {
matches++;
}
}
if(getSeverity() != null) {
checks++;
if(getSeverity().getId().equals(id.getSeverity())) {
matches++;
}
}
if(getResolution() != null) {
checks++;
if(getResolution().getId().equals(id.getResolution())) {
matches++;
}
}
if(getStatus() != null) {
checks++;
if(getStatus().getId().equals(id.getStatus())) {
matches++;
}
}
if(getPriority() != null) {
checks++;
if(getPriority().getId().equals(id.getPriority())) {
matches++;
}
}
if(getViewStatus() != null) {
checks++;
if(getViewStatus().getId().equals(id.getView_state())) {
matches++;
}
}
if(getProjectId() != null && (! getProjectId().equals(BigInteger.ZERO)) ) {
checks++;
if(getProjectId().equals(id.getProject())) {
matches++;
}
}
if(getLastUpdateAfter() != null) {
checks++;
if(getLastUpdateAfter().before(id.getLast_updated().getTime())) {
matches++;
}
}
if(getLastUpdateBefore() != null) {
checks++;
if(getLastUpdateBefore().after(id.getLast_updated().getTime())) {
matches++;
}
}
if(getSummaryFilter() != null && (! getSummaryFilter().isEmpty())) {
checks++;
Pattern p = null;
try {
p = Pattern.compile(getSummaryFilter());
} catch (PatternSyntaxException ex) {}
if(id.getSummary().contains(getSummaryFilter()) || (p != null && p.matcher(id.getSummary()).find())) {
matches++;
}
}
switch (combination) {
default:
case ALL:
return matches == checks;
case ANY:
return matches > 0;
}
}
/**
* Build a prefilter to do pre-filtering on the server side
*
* @return
*/
public FilterSearchData getAsServerFilter() {
FilterSearchData fsd = new FilterSearchData();
if (this.getCombination() == Combination.ALL) {
if (getReporter() != null) {
fsd.setReporter_id(new BigInteger[]{getReporter().getId()});
}
if (getAssignedTo() != null) {
fsd.setHandler_id(new BigInteger[]{getAssignedTo().getId()});
}
if (getCategory() != null) {
fsd.setCategory(new String[]{getCategory()});
}
if (getSeverity() != null) {
fsd.setSeverity_id(new BigInteger[]{getSeverity().getId()});
}
if (getResolution() != null) {
fsd.setResolution_id(new BigInteger[]{getResolution().getId()});
}
if (getStatus() != null) {
fsd.setStatus_id(new BigInteger[]{getStatus().getId()});
}
if (getPriority() != null) {
fsd.setPriority_id(new BigInteger[]{getPriority().getId()});
}
if (getViewStatus() != null) {
fsd.setView_state_id(new BigInteger[]{getViewStatus().getId()});
}
if (getProjectId() != null) {
fsd.setProject_id(new BigInteger[]{getProjectId()});
}
} else {
if (getProjectId() != null && getProjectId().compareTo(BigInteger.ZERO) != 0) {
fsd.setProject_id(new BigInteger[]{getProjectId()});
}
}
return fsd;
}
@Override
public int hashCode() {
int hash = 3;
hash = 71 * hash + Objects.hashCode(this.id);
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final MantisQuery other = (MantisQuery) obj;
if (!Objects.equals(this.id, other.id)) {
return false;
}
return true;
}
}