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.function.Consumer;
27  import java.util.function.Function;
28  import java.util.function.Predicate;
29  import java.util.stream.Stream;
30  
31  /**
32   * A sorted set that reports insertion collisions.
33   * @param <T> the type of object
34   */
35  public class ReportingSet<T> implements SortedSet<T> {
36      /** The sorted set this reporting set delegates to */
37      private final SortedSet<T> delegate;
38      /** The what to do when duplicates are found */
39      private Options duplicateOption = Options.IGNORE;
40      /** What level to log duplicate found messages */
41      private Log.Level duplicateLogLevel = Log.Level.WARN;
42      /** The message to log the duplicate with */
43      private Function<T, String> duplicateFmt = t -> String.format("Duplicate %s detected %s", t.getClass(), t);
44      /** The Options for duplicate processing */
45      public enum Options { OVERWRITE, IGNORE, FAIL }
46  
47      /**
48       * Constructs.
49       * @param delegate the SortedSet to delegate to.
50       */
51      public ReportingSet(final SortedSet<T> delegate) {
52          this.delegate = delegate;
53      }
54  
55      /**
56       * Sets the function to generate the log message.
57       * @param msgFmt A function to return the string to be displayed when a collision occurs.
58       * @return This for chaining.
59       */
60      public ReportingSet<T> setMsgFormat(final Function<T, String> msgFmt) {
61          duplicateFmt = msgFmt;
62          return this;
63      }
64  
65      /**
66       * If set true attempts to duplicate will throw an IllegalArgumentException.
67       * The default state is false;.
68       * @param state the state to set.
69       * @return this for chaining.
70       */
71      public ReportingSet<T> setDuplicateOption(final Options state) {
72          this.duplicateOption = state;
73          return this;
74      }
75  
76      /**
77       * Sets the log level that the reporting set will log at.
78       * if not set the default level is WARN.
79       * @param level the log level to use.
80       * @return this for chaining.
81       */
82      public ReportingSet<T> setLogLevel(final Log.Level level) {
83          this.duplicateLogLevel = level;
84          return this;
85      }
86  
87      private ReportingSet<T> sameConfig(final SortedSet<T> delegate) {
88          ReportingSet<T> result = delegate instanceof ReportingSet ? (ReportingSet<T>) delegate : new ReportingSet<>(delegate);
89          return result.setDuplicateOption(this.duplicateOption).setLogLevel(this.duplicateLogLevel);
90      }
91  
92      /**
93       * Adds the item if it is not present.  Does not report collisions.
94       * @param e the item to add.
95       * @return true if the item was added, false otherwise.
96       */
97      public boolean addIfNotPresent(final T e) {
98          return add(false, e);
99      }
100 
101     @Override
102     public boolean add(final T e) {
103         return add(true, e);
104     }
105 
106     /**
107      * Attempts to add an item.  Report failures if reportDup is true.
108      * @param reportDup the reporting flag.
109      * @param e the item to add
110      * @return true if the item was added.
111      */
112     private boolean add(final boolean reportDup, final T e) {
113         if (delegate.contains(e)) {
114             String msg = String.format("%s", ReportingSet.this.duplicateFmt.apply(e));
115             if (reportDup) {
116                 msg =  String.format("%s (action: %s)", msg, duplicateOption);
117                 DefaultLog.getInstance().log(duplicateLogLevel, msg);
118             }
119             switch (duplicateOption) {
120             case FAIL:
121                 throw new IllegalArgumentException(msg);
122             case IGNORE:
123                 return false;
124             case OVERWRITE:
125                 delegate.remove(e);
126                 return delegate.add(e);
127             }
128         }
129         return delegate.add(e);
130     }
131 
132     @Override
133     public boolean addAll(final Collection<? extends T> c) {
134         boolean updated = false;
135         for (T e : c) {
136             updated |= add(e);
137         }
138         return updated;
139     }
140 
141     public boolean addAllIfNotPresent(final Collection<? extends T> c) {
142         boolean updated = false;
143         for (T e : c) {
144             updated |= addIfNotPresent(e);
145         }
146         return updated;
147     }
148 
149     @Override
150     public void clear() {
151         delegate.clear();
152     }
153 
154     @Override
155     public Comparator<? super T> comparator() {
156         return delegate.comparator();
157     }
158 
159     @Override
160     public boolean contains(final Object o) {
161         return delegate.contains(o);
162     }
163 
164     @Override
165     public boolean containsAll(final Collection<?> c) {
166         return delegate.containsAll(c);
167     }
168 
169     @Override
170     public boolean equals(final Object o) {
171         return delegate.equals(o);
172     }
173 
174     @Override
175     public T first() {
176         return delegate.first();
177     }
178 
179     @Override
180     public void forEach(final Consumer<? super T> action) {
181         delegate.forEach(action);
182     }
183 
184     @Override
185     public int hashCode() {
186         return delegate.hashCode();
187     }
188 
189     @Override
190     public ReportingSet<T> headSet(final T toElement) {
191         return sameConfig(delegate.headSet(toElement));
192     }
193 
194     @Override
195     public boolean isEmpty() {
196         return delegate.isEmpty();
197     }
198 
199     @Override
200     public Iterator<T> iterator() {
201         return delegate.iterator();
202     }
203 
204     @Override
205     public T last() {
206         return delegate.last();
207     }
208 
209     @Override
210     public Stream<T> parallelStream() {
211         return delegate.parallelStream();
212     }
213 
214     @Override
215     public boolean remove(final Object o) {
216         return delegate.remove(o);
217     }
218 
219     @Override
220     public boolean removeAll(final Collection<?> c) {
221         return delegate.removeAll(c);
222     }
223 
224     @Override
225     public boolean removeIf(final Predicate<? super T> filter) {
226         return delegate.removeIf(filter);
227     }
228 
229     @Override
230     public boolean retainAll(final Collection<?> c) {
231         return delegate.retainAll(c);
232     }
233 
234     @Override
235     public int size() {
236         return delegate.size();
237     }
238 
239     @Override
240     public Spliterator<T> spliterator() {
241         return delegate.spliterator();
242     }
243 
244     @Override
245     public Stream<T> stream() {
246         return delegate.stream();
247     }
248 
249     @Override
250     public ReportingSet<T> subSet(final T fromElement, final T toElement) {
251         return sameConfig(delegate.subSet(fromElement, toElement));
252     }
253 
254     @Override
255     public ReportingSet<T> tailSet(final T fromElement) {
256         return sameConfig(delegate.tailSet(fromElement));
257     }
258 
259     @Override
260     public Object[] toArray() {
261         return delegate.toArray();
262     }
263 
264     @Override
265     public <T> T[] toArray(final T[] a) {
266         return delegate.toArray(a);
267     }
268 }