blob: 593b56c6134e994d4032b210bf708d881e9be177 [file] [log] [blame]
/**
* 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.tasklist.impl;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.netbeans.modules.tasklist.filter.TaskFilter;
import org.netbeans.modules.tasklist.trampoline.TaskGroup;
import org.netbeans.spi.tasklist.FileTaskScanner;
import org.netbeans.spi.tasklist.PushTaskScanner;
import org.netbeans.spi.tasklist.Task;
import org.openide.filesystems.FileObject;
import org.openide.util.WeakSet;
/**
* @author S. Aubrecht
* @author Tomas Stupka
*/
public class TaskList {
private TreeSet<Task> sortedTasks;
private ArrayList<Task> tasksList;
private Map<PushTaskScanner, List<Task>> pushScanner2tasks = new HashMap<PushTaskScanner, List<Task>>( 10 );
private Map<FileTaskScanner, List<Task>> fileScanner2tasks = new HashMap<FileTaskScanner, List<Task>>( 10 );
private Map<TaskGroup, List<Task>> group2tasks = new HashMap<TaskGroup,List<Task>>( 10 );
private final WeakSet<Listener> listeners = new WeakSet<Listener>( 2 );
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private Comparator<Task> comparator;
/** Creates a new instance of TaskList */
public TaskList() {
sortedTasks = new TreeSet<Task>(getComparator());
}
void setTasks( PushTaskScanner scanner, FileObject resource, List<? extends Task> tasks, TaskFilter filter ) throws IOException {
lock.writeLock().lock();
List<Task> removed = clear( scanner, resource );
Set<Task> tasksToAdd = null;
int currentCount = countTasks( scanner );
for( Task t : tasks ) {
if( filter.accept( t ) && !filter.isTaskCountLimitReached(currentCount) ) {
currentCount++;
if( sortedTasks.contains( t ) || (tasksToAdd != null && tasksToAdd.contains( t ) ) )
continue;
if( null == tasksToAdd )
tasksToAdd = new HashSet<Task>( tasks.size() );
List<Task> scannerTasks = pushScanner2tasks.get( scanner );
if( null == scannerTasks ) {
scannerTasks = new ArrayList<Task>();
pushScanner2tasks.put( scanner, scannerTasks );
}
TaskGroup group = Accessor.getGroup( t );
List<Task> groupTasks = group2tasks.get( group );
if( null == groupTasks ) {
groupTasks = new ArrayList<Task>();
group2tasks.put( group, groupTasks );
}
tasksToAdd.add( t );
scannerTasks.add( t );
groupTasks.add( t );
}
}
if( null != tasksToAdd ) {
addTasks( tasksToAdd );
}
lock.writeLock().unlock();
if( null != removed && !removed.isEmpty() )
fireTasksRemoved( removed );
if( null != tasksToAdd && !tasksToAdd.isEmpty() )
fireTasksAdded(new ArrayList(tasksToAdd));
}
void clear( PushTaskScanner scanner ) {
lock.writeLock().lock();
List<Task> toRemove = pushScanner2tasks.get( scanner );
pushScanner2tasks.remove( scanner );
if( null != toRemove ) {
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
removeTasks( toRemove );
}
lock.writeLock().unlock();
if( null != toRemove && !toRemove.isEmpty() ) {
fireTasksRemoved( toRemove );
}
}
private int countTasks( PushTaskScanner scanner ) {
List<Task> tasks = pushScanner2tasks.get( scanner );
return null == tasks ? 0 : tasks.size();
}
private List<Task> clear( PushTaskScanner scanner, FileObject resource ) {
Set<Task> toRemove = null;
List<Task> tasks = pushScanner2tasks.get( scanner );
if( null != tasks ) {
if( null == resource ) {
toRemove = new HashSet<Task>();
toRemove.addAll(tasks);
} else {
for( Task t : tasks ) {
if( resource.equals( Accessor.getFile( t ) ) ) {
if( null == toRemove )
toRemove = new HashSet<Task>();
toRemove.add( t );
}
}
}
}
if( null != toRemove ) {
removeTasks( toRemove );
tasks.removeAll( toRemove );
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
}
return toRemove == null ? null : new LinkedList(toRemove);
}
void update( FileTaskScanner scanner, FileObject resource, List<Task> newTasks, TaskFilter filter ) {
lock.writeLock().lock();
List<Task> removed = clear( scanner, resource );
Set<Task> tasksToAdd = new HashSet<Task>(newTasks.size());
for( Task t : newTasks ) {
if( sortedTasks.contains( t ) || tasksToAdd.contains( t ) )
continue;
if( !filter.isTaskCountLimitReached( countTasks( scanner ) ) && filter.accept( t ) ) {
List<Task> scannerTasks = fileScanner2tasks.get( scanner );
if( null == scannerTasks ) {
scannerTasks = new ArrayList<Task>();
fileScanner2tasks.put( scanner, scannerTasks );
}
TaskGroup group = Accessor.getGroup( t );
List<Task> groupTasks = group2tasks.get( group );
if( null == groupTasks ) {
groupTasks = new ArrayList<Task>();
group2tasks.put( group, groupTasks );
}
scannerTasks.add( t );
groupTasks.add( t );
tasksToAdd.add( t );
}
}
if( !tasksToAdd.isEmpty() ) {
addTasks( tasksToAdd );
}
lock.writeLock().unlock();
if( null != removed && !removed.isEmpty() )
fireTasksRemoved( removed );
if( !tasksToAdd.isEmpty() )
fireTasksAdded(new ArrayList<Task>(tasksToAdd));
}
public int size() {
int retValue = 0;
lock.readLock().lock();
retValue = sortedTasks.size();
lock.readLock().unlock();
return retValue;
}
public List<? extends Task> getTasks() {
lock.readLock().lock();
try {
return new ArrayList<Task>( sortedTasks );
} finally {
lock.readLock().unlock();
}
}
int countTasks( FileTaskScanner scanner ) {
List<Task> tasks = fileScanner2tasks.get( scanner );
return null == tasks ? 0 : tasks.size();
}
public int countTasks( TaskGroup group ) {
List<Task> groupTasks = group2tasks.get( group );
return null == groupTasks ? 0 : groupTasks.size();
}
public Task getTask( int index ) {
Task retValue = null;
lock.readLock().lock();
if( index >= 0 && index < sortedTasks.size() )
retValue = getTasksList().get( index );
lock.readLock().unlock();
return retValue;
}
void clear( FileTaskScanner scanner ) {
lock.writeLock().lock();
List<Task> toRemove = fileScanner2tasks.get( scanner );
fileScanner2tasks.remove( scanner );
if( null != toRemove ) {
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
removeTasks( toRemove );
}
lock.writeLock().unlock();
if( null != toRemove && !toRemove.isEmpty() ) {
fireTasksRemoved( toRemove );
}
}
void clear( FileTaskScanner scanner, FileObject... resources ) throws IOException {
lock.readLock().lock();
ArrayList<Task> toRemove = null;
List<Task> tasks = fileScanner2tasks.get( scanner );
if( null != tasks ) {
for( Task t : tasks ) {
for( FileObject rc : resources ) {
if( rc.equals( Accessor.getFile( t ) ) ) {
if( null == toRemove )
toRemove = new ArrayList<Task>( resources.length );
toRemove.add( t );
}
}
}
}
lock.readLock().unlock();
if( null != toRemove && !toRemove.isEmpty() ) {
lock.writeLock().lock();
removeTasks( toRemove );
tasks.removeAll( toRemove );
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
lock.writeLock().unlock();
fireTasksRemoved( toRemove );
}
}
private List<Task> clear( FileTaskScanner scanner, FileObject resource ) {
List<Task> tasks = fileScanner2tasks.get( scanner );
if( null == tasks )
return null;
Set<Task> toRemove = null;
for( Task t : tasks ) {
if( resource.equals( Accessor.getFile( t ) ) ) {
if( null == toRemove )
toRemove = new HashSet<Task>();
toRemove.add( t );
}
}
if( null != toRemove ) {
removeTasks( toRemove );
tasks.removeAll( toRemove );
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
}
return toRemove == null ? null : new LinkedList(toRemove);
}
void clear( FileObject resource ) {
List<Task> toRemove = null;
lock.writeLock().lock();
for( List<Task> scannerTasks : fileScanner2tasks.values() ) {
for( Task t : scannerTasks ) {
if( resource.equals( Accessor.getFile(t) ) ) {
if( null == toRemove )
toRemove = new LinkedList<Task>();
toRemove.add( t );
}
}
}
if( null != toRemove ) {
removeTasks( toRemove );
for( List<Task> scannerTasks : fileScanner2tasks.values() ) {
scannerTasks.removeAll( toRemove );
}
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
}
lock.writeLock().unlock();
if( null != toRemove ) {
fireTasksRemoved( toRemove );
}
}
void clear() {
lock.writeLock().lock();
sortedTasks.clear();
tasksList = null;
fileScanner2tasks.clear();
pushScanner2tasks.clear();
group2tasks.clear();
lock.writeLock().unlock();
fireCleared();
}
void clearDeletedFiles() {
lock.writeLock().lock();
LinkedList<Task> toRemove = new LinkedList<Task>();
for( Task t : sortedTasks ) {
FileObject fo = Accessor.getFile(t);
if( null != fo && !fo.isValid() )
toRemove.add(t);
}
if( !toRemove.isEmpty() ) {
removeTasks( toRemove );
for( List<Task> scannerTasks : fileScanner2tasks.values() ) {
scannerTasks.removeAll( toRemove );
}
for( List<Task> groupTasks : group2tasks.values() ) {
groupTasks.removeAll( toRemove );
}
}
lock.writeLock().unlock();
if( !toRemove.isEmpty() ) {
fireTasksRemoved( toRemove );
}
}
public void addListener( Listener l ) {
synchronized( listeners ) {
listeners.add( l );
}
}
public void removeListener( Listener l ) {
synchronized( listeners ) {
listeners.remove( l );
}
}
public int indexOf( Task t ) {
lock.readLock().lock();
int idx = getTasksList().indexOf(t);
lock.readLock().unlock();
return idx;
}
private Comparator<Task> getComparator() {
if( null == comparator )
comparator = TaskComparator.getDefault();
return comparator;
}
public void setComparator( Comparator<Task> comparator ) {
if( getComparator().equals( comparator ) ) {
return;
}
lock.writeLock().lock();
this.comparator = comparator;
TreeSet<Task> s = sortedTasks;
sortedTasks = new TreeSet<Task>(comparator);
addTasks(s);
lock.writeLock().unlock();
}
private void fireTasksAdded( List<Task> tasks ) {
TaskList.Listener[] tmp;
synchronized( listeners ) {
tmp = listeners.toArray(new TaskList.Listener[listeners.size()]);
}
for ( Listener l : tmp ) {
l.tasksAdded( tasks );
}
}
private void fireTasksRemoved( List<Task> tasks ) {
TaskList.Listener[] tmp;
synchronized( listeners ) {
tmp = listeners.toArray(new TaskList.Listener[listeners.size()]);
}
for ( Listener l : tmp ) {
l.tasksRemoved( tasks );
}
}
private void fireCleared() {
TaskList.Listener[] tmp;
synchronized( listeners ) {
tmp = listeners.toArray(new TaskList.Listener[listeners.size()]);
}
for( Listener l : tmp) {
l.cleared();
}
}
private List<Task> getTasksList() {
if(tasksList == null) {
tasksList = new ArrayList<Task>(sortedTasks);
}
return tasksList;
}
private void addTasks(Collection<Task> tasksToAdd) {
sortedTasks.addAll( tasksToAdd );
tasksList = null;
}
private void removeTasks(Collection<Task> toRemove) {
sortedTasks.removeAll( toRemove );
tasksList = null;
}
public static interface Listener {
void tasksAdded( List<? extends Task> tasks );
void tasksRemoved( List<? extends Task> tasks );
void cleared();
}
}