View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   */
19  package org.apache.rat.utils;
20  
21  import java.util.Collection;
22  import java.util.Comparator;
23  import java.util.Iterator;
24  import java.util.SortedSet;
25  import java.util.Spliterator;
26  import java.util.TreeSet;
27  import java.util.function.Consumer;
28  import java.util.function.Function;
29  import java.util.function.Predicate;
30  import java.util.stream.Stream;
31  
32  /**
33   * A sorted set that reports insertion collisions.
34   * @param <T> the type of object
35   */
36  public class ReportingSet<T> implements SortedSet<T> {
37      private final SortedSet<T> delegate;
38      private Options duplicateOption = Options.IGNORE;
39      private Log.Level duplicateLogLevel = Log.Level.WARN;
40      private Log log = DefaultLog.INSTANCE;
41      private Function<T,String> duplicateFmt = (t) -> String.format("Duplicate %s (%s) detected %s", t.getClass(), t);
42  
43      public enum Options { OVERWRITE, IGNORE, FAIL }
44      
45      /**
46       * Constructor.
47       * Creates a TreeSet of type T.
48       */
49      public ReportingSet() {
50          this(new TreeSet<T>());
51      }
52      
53      /**
54       * Constructs.
55       * 
56       * @param delegate the SortedSet to delegate to.
57       */
58      public ReportingSet(SortedSet<T> delegate) {
59          this.delegate = delegate;
60      }
61  
62      /**
63       * Sets the function to generate the log message.
64       * @param msgFmt A function to return the string to be displayed when a collision occurs.
65       * @return This for chaining.
66       */
67      public ReportingSet<T> setMsgFormat(Function<T,String> msgFmt) {
68          duplicateFmt = msgFmt;
69          return this;
70      }
71      /**
72       * If set true attempts to duplicate will throw an IllegalArgumentException.
73       * The default state is false;.
74       * @param state the state to set.
75       * @return this for chaining.
76       */
77      public ReportingSet<T> setDuplicateOption(Options state) {
78          this.duplicateOption = state;
79          return this;
80      }
81  
82      /**
83       * Sets the log that the reporting set will log to.
84       * if not set the DefaultLog is used.
85       * @param log the Log implementation to use.
86       * @return this for chaining.
87       */
88      public ReportingSet<T> setLog(Log log) {
89          this.log = log;
90          return this;
91      }
92  
93      /**
94       * Sets the log level that the reporting set will log at.
95       * if not set the default level is WARN.
96       * @param level the log level to use.
97       * @return this for chaining.
98       */
99      public ReportingSet<T> setLogLevel(Log.Level level) {
100         this.duplicateLogLevel = level;
101         return this;
102     }
103 
104     private ReportingSet<T> sameConfig(SortedSet<T> delegate) {
105         ReportingSet<T> result = delegate instanceof ReportingSet ? (ReportingSet<T>) delegate : new ReportingSet<>(delegate);
106         return result.setDuplicateOption(this.duplicateOption).setLog(this.log).setLogLevel(this.duplicateLogLevel);
107     }
108 
109     /**
110      * Adds the item if it is not present.  Does not report collisions.
111      * @param e the item to add.
112      * @return true if the item was added, false otherwise.
113      */
114     public boolean addIfNotPresent(T e) {
115         return add(false, e);
116     }
117     
118     @Override
119     public boolean add(T e) {
120         return add(true, e);
121     }
122     
123     /**
124      * Attempts to add an item.  Report failures if reportDup is true.
125      * @param reportDup the reporting flag.
126      * @param e the item to add
127      * @return true if the item was added.
128      */
129     private boolean add(boolean reportDup, T e) {
130         if (delegate.contains(e)) {
131             String msg = String.format("%s",ReportingSet.this.duplicateFmt.apply(e));
132             if (reportDup) {
133                 msg =  String.format( "%s (action: %s)", msg, duplicateOption);
134                 log.log(duplicateLogLevel, msg);
135             }
136             switch (duplicateOption) {
137             case FAIL:
138                 throw new IllegalArgumentException(msg);
139             case IGNORE:
140                 return false;
141             case OVERWRITE:
142                 delegate.remove(e);
143                 return delegate.add(e);
144             }
145         }
146         return delegate.add(e);
147     }
148 
149     @Override
150     public boolean addAll(Collection<? extends T> c) {
151         boolean updated = false;
152         for (T e : c) {
153             updated |= add(e);
154         }
155         return updated;
156     }
157 
158  
159     public boolean addAllIfNotPresent(Collection<? extends T> c) {
160         boolean updated = false;
161         for (T e : c) {
162             updated |= addIfNotPresent(e);
163         }
164         return updated;
165     }
166     
167     @Override
168     public void clear() {
169         delegate.clear();
170     }
171 
172     @Override
173     public Comparator<? super T> comparator() {
174         return delegate.comparator();
175     }
176 
177     @Override
178     public boolean contains(Object o) {
179         return delegate.contains(o);
180     }
181 
182     @Override
183     public boolean containsAll(Collection<?> c) {
184         return delegate.containsAll(c);
185     }
186 
187     @Override
188     public boolean equals(Object o) {
189         return delegate.equals(o);
190     }
191 
192     @Override
193     public T first() {
194         return delegate.first();
195     }
196 
197     @Override
198     public void forEach(Consumer<? super T> action) {
199         delegate.forEach(action);
200     }
201 
202     @Override
203     public int hashCode() {
204         return delegate.hashCode();
205     }
206 
207     @Override
208     public ReportingSet<T> headSet(T toElement) {
209         return sameConfig(delegate.headSet(toElement));
210     }
211 
212     @Override
213     public boolean isEmpty() {
214         return delegate.isEmpty();
215     }
216 
217     @Override
218     public Iterator<T> iterator() {
219         return delegate.iterator();
220     }
221 
222     @Override
223     public T last() {
224         return delegate.last();
225     }
226 
227     @Override
228     public Stream<T> parallelStream() {
229         return delegate.parallelStream();
230     }
231 
232     @Override
233     public boolean remove(Object o) {
234         return delegate.remove(o);
235     }
236 
237     @Override
238     public boolean removeAll(Collection<?> c) {
239         return delegate.removeAll(c);
240     }
241 
242     @Override
243     public boolean removeIf(Predicate<? super T> filter) {
244         return delegate.removeIf(filter);
245     }
246 
247     @Override
248     public boolean retainAll(Collection<?> c) {
249         return delegate.retainAll(c);
250     }
251 
252     @Override
253     public int size() {
254         return delegate.size();
255     }
256 
257     @Override
258     public Spliterator<T> spliterator() {
259         return delegate.spliterator();
260     }
261 
262     @Override
263     public Stream<T> stream() {
264         return delegate.stream();
265     }
266 
267     @Override
268     public ReportingSet<T> subSet(T fromElement, T toElement) {
269         return sameConfig(delegate.subSet(fromElement, toElement));
270     }
271 
272     @Override
273     public ReportingSet<T> tailSet(T fromElement) {
274         return sameConfig(delegate.tailSet(fromElement));
275     }
276 
277     @Override
278     public Object[] toArray() {
279         return delegate.toArray();
280     }
281 
282     @Override
283     public <T> T[] toArray(T[] a) {
284         return delegate.toArray(a);
285     }
286 }