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