race condition
[idea/community.git] / platform / util / src / com / intellij / util / UniqueResultsQuery.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.intellij.util;
18
19 import com.intellij.util.containers.ConcurrentHashSet;
20 import gnu.trove.TObjectHashingStrategy;
21 import org.jetbrains.annotations.NotNull;
22
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.Set;
27 import java.util.concurrent.CopyOnWriteArrayList;
28
29 /**
30  * @author max
31  */
32 public class UniqueResultsQuery<T, M> implements Query<T> {
33   private final Query<T> myOriginal;
34   private final TObjectHashingStrategy<M> myHashingStrategy;
35   private final Function<T, M> myMapper;
36
37   public static final Function ID = new Function() {
38     public Object fun(Object o) {
39       return o;
40     }
41   };
42
43   public UniqueResultsQuery(final Query<T> original) {
44     //noinspection unchecked
45     this(original, TObjectHashingStrategy.CANONICAL, ID);
46   }
47
48   public UniqueResultsQuery(final Query<T> original, TObjectHashingStrategy<M> hashingStrategy) {
49     //noinspection unchecked
50     this(original, hashingStrategy, ID);
51   }
52
53   public UniqueResultsQuery(final Query<T> original, TObjectHashingStrategy<M> hashingStrategy, Function<T, M> mapper) {
54     myOriginal = original;
55     myHashingStrategy = hashingStrategy;
56     myMapper = mapper;
57   }
58
59   public T findFirst() {
60     return myOriginal.findFirst();
61   }
62
63   public boolean forEach(@NotNull final Processor<T> consumer) {
64     Collection<M> collection = process(consumer);
65     return collection != null;
66   }
67
68   // returns null if canceled
69   private Collection<M> process(final Processor<T> consumer) {
70     final Set<M> processedElements = new ConcurrentHashSet<M>(myHashingStrategy);
71     boolean success = myOriginal.forEach(new Processor<T>() {
72       public boolean process(final T t) {
73         return !processedElements.add(myMapper.fun(t)) || consumer.process(t);
74       }
75     });
76     return success ? processedElements : null;
77   }
78
79   @NotNull
80   public Collection<T> findAll() {
81     if (myMapper == ID) {
82       Collection<M> collection = process(CommonProcessors.<T>alwaysTrue());
83       //noinspection unchecked
84       return collection == null ? Collections.<T>emptyList() : (Collection<T>)collection;
85     }
86     else {
87       final CommonProcessors.CollectProcessor<T> processor = new CommonProcessors.CollectProcessor<T>(new CopyOnWriteArrayList<T>());
88       forEach(processor);
89       return processor.getResults();
90     }
91   }
92
93   public T[] toArray(final T[] a) {
94     return findAll().toArray(a);
95   }
96
97   public Iterator<T> iterator() {
98     return findAll().iterator();
99   }
100 }