org.netbeans.modules.bugtracking.RepositoryImpl Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.bugtracking;
import java.awt.Image;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import static org.netbeans.modules.bugtracking.BugtrackingOwnerSupport.ContextType.SELECTED_FILE_AND_ALL_PROJECTS;
import org.netbeans.modules.bugtracking.api.Repository;
import org.netbeans.modules.bugtracking.spi.*;
import org.netbeans.modules.team.spi.NBRepositoryProvider;
import org.netbeans.modules.team.spi.OwnerInfo;
import org.netbeans.modules.team.spi.TeamBugtrackingConnector;
import org.openide.util.Utilities;
/**
*
* Represents a bug tracking repository (server)
*
* @author Tomas Stupka
*/
public final class RepositoryImpl {
private static final Logger LOG = Logger.getLogger("org.netbeans.modules.bugtracking.Repository"); // NOI18N
public static final String EVENT_QUERY_LIST_CHANGED = RepositoryProvider.EVENT_QUERY_LIST_CHANGED;
public static final String EVENT_UNSUBMITTED_ISSUES_CHANGED = RepositoryProvider.EVENT_UNSUBMITTED_ISSUES_CHANGED;
/**
* RepositoryProvider's attributes have changed, e.g. name, url, etc.
* Old and new value are maps of changed doubles: attribute-name / attribute-value.
* Old value can be null in case the repository is created.
*/
public static final String EVENT_ATTRIBUTES_CHANGED = "bugtracking.repository.attributes.changed"; //NOI18N
public static final String ATTRIBUTE_URL = "repository.attribute.url"; //NOI18N
public static final String ATTRIBUTE_DISPLAY_NAME = "repository.attribute.displayName"; //NOI18N
private final PropertyChangeSupport support;
private final RepositoryProvider repositoryProvider;
private final IssueProvider issueProvider;
private final QueryProvider queryProvider;
private final IssueStatusProvider issueStatusProvider;
private final IssueScheduleProvider issueSchedulingProvider;
private final IssuePriorityProvider issuePriorityProvider;
private final R r;
private final WrapperMap issueMap = new WrapperMap() {
@Override
IssueImpl createWrapper(I d) {
return new IssueImpl(RepositoryImpl.this, issueProvider, d);
}
};
private final WrapperMap queryMap = new WrapperMap() {
@Override
QueryImpl createWrapper(Q d) {
return new QueryImpl(RepositoryImpl.this, queryProvider, issueProvider, d);
}
};
private Repository repository;
private IssuePrioritySupport prioritySupport;
private final IssueFinder issueFinder;
public RepositoryImpl(
final R r,
RepositoryProvider repositoryProvider,
QueryProvider queryProvider,
IssueProvider issueProvider,
IssueStatusProvider issueStatusProvider,
IssueScheduleProvider issueSchedulingProvider,
IssuePriorityProvider issuePriorityProvider,
IssueFinder issueFinder)
{
this.repositoryProvider = repositoryProvider;
this.issueProvider = issueProvider;
this.queryProvider = queryProvider;
this.issueStatusProvider = issueStatusProvider;
this.issueSchedulingProvider = issueSchedulingProvider;
this.issuePriorityProvider = issuePriorityProvider;
this.issueFinder = issueFinder;
this.r = r;
support = new PropertyChangeSupport(this);
repositoryProvider.addPropertyChangeListener(r, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(RepositoryProvider.EVENT_QUERY_LIST_CHANGED.equals(evt.getPropertyName())) {
if(LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "firing query list change {0} - rImpl: {1} - r: {2}", new Object[]{getDisplayName(), this, r}); // NOI18N
}
Collection queries = new ArrayList<>(getQueries());
synchronized(queryMap) {
List toRemove = new LinkedList();
for(Entry> e : queryMap.entrySet()) {
boolean contains = false;
for(QueryImpl q : queries) {
QueryImpl cachedQuery = e.getValue().get();
if( cachedQuery != null && cachedQuery.isData(q.getData()) ) {
contains = true;
break;
}
}
if(!contains) {
toRemove.add(e.getKey());
}
}
queryMap.keySet().removeAll(toRemove);
}
fireQueryListChanged();
} else if (RepositoryProvider.EVENT_UNSUBMITTED_ISSUES_CHANGED.equals(evt.getPropertyName())) {
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "firing unsubmitted issues change {0} - rImpl: {1} - r: {2}", //NOI18N
new Object[] { getDisplayName(), this, r } );
}
fireUnsubmittedIssuesChanged();
}
}
});
if(LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "created repository {0} - rImpl: {1} - r: {2}", new Object[]{getDisplayName(), this, r}); // NOI18N
}
}
public synchronized Repository getRepository() {
if(repository == null) {
repository = APIAccessor.IMPL.createRepository(this);
}
return repository;
}
public IssueFinder getIssueFinder() {
return issueFinder;
}
/**
* Returns the icon for this repository
* @return
*/
public Image getIcon() {
return repositoryProvider.getIcon(r);
}
/**
* Returns the display name for this repository
* @return
*/
public String getDisplayName() {
RepositoryInfo info = repositoryProvider.getInfo(r);
return info != null ? info.getDisplayName() : null;
}
/**
* Returns the tooltip for this repository
* @return
*/
public String getTooltip() {
RepositoryInfo info = repositoryProvider.getInfo(r);
return info != null ? info.getTooltip() : null;
}
/**
* Returns a unique ID for this repository
*
* @return
*/
public String getId() { // XXX API its either Id or ID
return getInfo().getID();
}
public RepositoryInfo getInfo() {
return repositoryProvider.getInfo(r);
}
/**
* Returns the repositories url
* @return
*/
public String getUrl() {
return getInfo().getUrl();
}
/**
* Returns an issue with the given ID
*
* @param id
* @return
*/
public Collection getIssueImpls(String... ids) {
Collection is = repositoryProvider.getIssues(r, ids);
if(is == null || is.isEmpty()) {
return Collections.emptyList();
}
List ret = new ArrayList<>(is.size());
for (I i : is) {
IssueImpl impl = getIssue(i);
if(impl != null) {
ret.add(impl);
}
}
return ret;
}
R getData() {
return r;
}
public QueryImpl createNewQuery() {
setLooseAssociation();
return getQuery(repositoryProvider.createQuery(r));
}
public IssueImpl createNewIssue() {
setLooseAssociation();
I issueData = repositoryProvider.createIssue(r);
return getIssue(issueData);
}
public IssueImpl createNewIssue(String summary, String description) {
setLooseAssociation();
I issueData = repositoryProvider.createIssue(r, summary, description);
return getIssue(issueData);
}
public RepositoryProvider getProvider() {
return repositoryProvider;
}
public Collection simpleSearch(String criteria) {
Collection issues = repositoryProvider.simpleSearch(r, criteria);
List ret = new ArrayList<>(issues.size());
for (I i : issues) {
ret.add(getIssue(i));
}
return ret;
}
public Collection getQueries() {
Collection queries = repositoryProvider.getQueries(r);
List ret = new ArrayList(queries.size());
for (Q q : queries) {
ret.add(getQuery(q));
}
return ret;
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
IssueStatusProvider getStatusProvider() {
return issueStatusProvider;
}
IssueScheduleProvider getSchedulingProvider() {
return issueSchedulingProvider;
}
IssuePriorityProvider getPriorityProvider() {
return issuePriorityProvider;
}
public IssuePriorityInfo[] getPriorityInfos() {
return issuePriorityProvider != null ? issuePriorityProvider.getPriorityInfos() : new IssuePriorityInfo[0];
}
String getPriorityName(I i) {
return issuePriorityProvider != null ?
getPrioritySupport().getName(issuePriorityProvider.getPriorityID(i)) :
""; // NOI18N
}
Image getPriorityIcon(I i) {
Image icon = null;
if(issuePriorityProvider != null) {
icon = getPrioritySupport().getIcon(issuePriorityProvider.getPriorityID(i));
}
if(icon == null) {
icon = IssuePrioritySupport.getDefaultIcon();
}
return icon;
}
public void setIssueContext(I i, OwnerInfo info) {
assert repositoryProvider instanceof NBRepositoryProvider;
if(repositoryProvider instanceof NBRepositoryProvider) {
((NBRepositoryProvider)repositoryProvider).setIssueOwnerInfo(i, info);
}
}
public void setQueryContext(Q q, OwnerInfo info) {
assert repositoryProvider instanceof NBRepositoryProvider;
if(repositoryProvider instanceof NBRepositoryProvider) {
((NBRepositoryProvider)repositoryProvider).setQueryOwnerInfo(q, info);
}
}
/**
* Notify listeners on this repository that a query was either removed or saved
* XXX make use of new/old value
*/
void fireQueryListChanged() {
support.firePropertyChange(Repository.EVENT_QUERY_LIST_CHANGED, null, null);
}
/**
* Notify listeners on this repository that some of repository's attributes have changed.
* @param oldValue map of old attributes
* @param newValue map of new attributes
*/
void fireAttributesChanged (java.util.Map oldAttributes, java.util.Map newAttributes) {
LinkedList equalAttributes = new LinkedList();
// find unchanged values
for (Map.Entry e : newAttributes.entrySet()) {
String key = e.getKey();
Object value = e.getValue();
Object oldValue = oldAttributes.get(key);
if ((value == null && oldValue == null) || (value != null && value.equals(oldValue))) {
equalAttributes.add(key);
}
}
// remove unchanged values
for (String equalAttribute : equalAttributes) {
if (oldAttributes != null) {
oldAttributes.remove(equalAttribute);
}
newAttributes.remove(equalAttribute);
}
if (!newAttributes.isEmpty()) {
support.firePropertyChange(new java.beans.PropertyChangeEvent(this, EVENT_ATTRIBUTES_CHANGED, oldAttributes, newAttributes));
}
}
public void applyChanges() {
HashMap oldAttributes = createAttributesMap();
repositoryProvider.getController(getData()).applyChanges();
HashMap newAttributes = createAttributesMap();
fireAttributesChanged(oldAttributes, newAttributes);
}
public void cancelChanges() {
repositoryProvider.getController(getData()).cancelChanges();
}
private HashMap createAttributesMap () {
HashMap attributes = new HashMap(2);
// XXX add more if requested
if(getInfo() != null) {
attributes.put(ATTRIBUTE_DISPLAY_NAME, getDisplayName());
attributes.put(ATTRIBUTE_URL, getUrl());
}
return attributes;
}
public String getConnectorId() {
return getInfo().getConnectorId();
}
public synchronized IssueImpl getIssue(I i) {
return issueMap.getWrapper(i);
}
public QueryImpl getQuery(Q q) {
return queryMap.getWrapper(q);
}
public boolean isTeamRepository() {
return getInfo().getValue(TeamBugtrackingConnector.TEAM_PROJECT_NAME) != null;
}
public boolean isMutable() {
DelegatingConnector dc = BugtrackingManager.getInstance().getConnector(getConnectorId());
assert dc != null;
return dc.providesRepositoryManagement();
}
public boolean canAttachFiles() {
return repositoryProvider.canAttachFiles(r);
}
public void remove() {
RepositoryRegistry.getInstance().removeRepository(this);
repositoryProvider.removed(r);
}
public RepositoryController getController() {
return repositoryProvider.getController(r);
}
//////////////////////
// Unsubmitted issues
//////////////////////
public Collection getUnsubmittedIssues () {
Collection issues = issueStatusProvider != null ? issueStatusProvider.getUnsubmittedIssues(r) : null;
if (issues == null || issues.isEmpty()) {
return Collections.emptyList();
}
List ret = new ArrayList<>(issues.size());
for (I i : issues) {
IssueImpl impl = getIssue(i);
if(impl != null) {
ret.add(impl);
}
}
return ret;
}
private void fireUnsubmittedIssuesChanged() {
support.firePropertyChange(EVENT_UNSUBMITTED_ISSUES_CHANGED, null, null);
}
private synchronized IssuePrioritySupport getPrioritySupport() {
if(prioritySupport == null) {
prioritySupport = new IssuePrioritySupport(issuePriorityProvider.getPriorityInfos());
}
return prioritySupport;
}
private void setLooseAssociation() {
BugtrackingOwnerSupport.getInstance().setLooseAssociation(SELECTED_FILE_AND_ALL_PROJECTS, this);
}
private int fakeIdCounter = 0;
String getNextFakeIssueID() {
return getConnectorId() + "<=>" + getId() + "<=>" + (--fakeIdCounter);
}
private boolean hasNumericIDs = true;
private boolean hasNumericPrefix = true;
private boolean hasNumericSufix = true;
int compareID(String id1, String id2) {
if(hasNumericIDs) {
try {
return compare(id1, id2);
} catch (NumberFormatException e) {
hasNumericIDs = false;
}
}
int idx1 = id1.lastIndexOf("-");
int idx2 = id2.lastIndexOf("-");
if(idx1 > -1 && idx2 > -1) {
if(hasNumericPrefix) {
try {
int i = compare(getPrefix(id1, idx1), getPrefix(id2, idx2));
return i != 0 ? i : getSufix(id1, idx1).compareTo(getSufix(id2, idx2));
} catch (NumberFormatException e) {
hasNumericPrefix = false;
}
} else if(hasNumericSufix) {
try {
int i = getPrefix(id1, idx1).compareTo(getPrefix(id2, idx2));
return i != 0 ? i : compare(getSufix(id1, idx1), getSufix(id2, idx2));
} catch (NumberFormatException e) {
hasNumericSufix = false;
}
}
}
return id1.compareTo(id2);
}
protected String getSufix(String id1, int idx1) {
return id1.substring(idx1 + 1);
}
private String getPrefix(String id1, int idx1) {
return id1.substring(0, idx1);
}
protected int compare(String id1, String id2) throws NumberFormatException {
long l1 = Long.parseLong(id1);
long l2 = Long.parseLong(id2);
return Long.compare(l1, l2);
}
private abstract class WrapperMap extends HashMap> {
public synchronized WRAPPER getWrapper(DATA d) {
if(d == null) {
return null;
}
WeakReference ref = get(d);
WRAPPER w = ref != null ? ref.get() : null;
if(w == null) {
w = createWrapper(d);
put(d, new MapReference(d, w));
}
return w;
}
public WeakReference get(DATA key, WRAPPER w) {
return super.put(key, new MapReference(key, w));
}
abstract WRAPPER createWrapper(DATA d);
private class MapReference extends WeakReference implements Runnable {
private final DATA key;
public MapReference(DATA d, WRAPPER w) {
super(w, Utilities.activeReferenceQueue());
this.key = d;
}
@Override
public void run() {
WrapperMap.this.remove(key);
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy