OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 149 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 149 Rev 177
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
9
 * language governing permissions and limitations under the License.
10
 * 
10
 * 
11
 * When distributing the software, include this License Header Notice in each file.
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
12
 */
13
 
13
 
14
 package org.openconcerto.utils.cc;
14
 package org.openconcerto.utils.cc;
15
 
15
 
16
import org.openconcerto.utils.CollectionUtils;
16
import org.openconcerto.utils.CollectionUtils;
17
import org.openconcerto.utils.CompareUtils;
17
import org.openconcerto.utils.CompareUtils;
18
import org.openconcerto.utils.Tuple2;
18
import org.openconcerto.utils.Tuple2;
19
 
19
 
-
 
20
import java.util.Arrays;
20
import java.util.Collection;
21
import java.util.Collection;
21
import java.util.Collections;
22
import java.util.Collections;
22
import java.util.HashSet;
23
import java.util.HashSet;
23
import java.util.Set;
24
import java.util.Set;
24
 
25
 
25
import net.jcip.annotations.Immutable;
26
import net.jcip.annotations.Immutable;
26
 
27
 
27
/**
28
/**
28
 * A class to specify which objects to include or exclude.
29
 * A class to specify which objects to include or exclude.
29
 *
30
 *
30
 * @author Sylvain
31
 * @author Sylvain
31
 * @param <T> type of items
32
 * @param <T> type of items
32
 * @see #isIncluded(Object)
33
 * @see #isIncluded(Object)
33
 */
34
 */
34
@Immutable
35
@Immutable
35
public class IncludeExclude<T> {
36
public class IncludeExclude<T> {
36
 
37
 
37
    static private final IncludeExclude<Object> EMPTY = new IncludeExclude<Object>(Collections.<Object> emptySet(), Collections.<Object> emptySet(), true);
38
    static private final IncludeExclude<Object> EMPTY = new IncludeExclude<Object>(Collections.<Object> emptySet(), Collections.<Object> emptySet(), true);
38
    static private final IncludeExclude<Object> FULL = new IncludeExclude<Object>(null, Collections.<Object> emptySet(), true);
39
    static private final IncludeExclude<Object> FULL = new IncludeExclude<Object>(null, Collections.<Object> emptySet(), true);
39
 
40
 
40
    @SuppressWarnings("unchecked")
41
    @SuppressWarnings("unchecked")
41
    public static <T> IncludeExclude<T> getEmpty() {
42
    public static <T> IncludeExclude<T> getEmpty() {
42
        return (IncludeExclude<T>) EMPTY;
43
        return (IncludeExclude<T>) EMPTY;
43
    }
44
    }
44
 
45
 
45
    @SuppressWarnings("unchecked")
46
    @SuppressWarnings("unchecked")
46
    public static <T> IncludeExclude<T> getFull() {
47
    public static <T> IncludeExclude<T> getFull() {
47
        return (IncludeExclude<T>) FULL;
48
        return (IncludeExclude<T>) FULL;
48
    }
49
    }
49
 
50
 
-
 
51
    @SafeVarargs
-
 
52
    public static <T> IncludeExclude<T> getNormalized(final T... includes) {
-
 
53
        return getNormalized(Arrays.asList(includes));
-
 
54
    }
-
 
55
 
50
    public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes) {
56
    public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes) {
51
        return getNormalized(includes, Collections.<T> emptySet());
57
        return getNormalized(includes, Collections.<T> emptySet());
52
    }
58
    }
53
 
59
 
54
    public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes, final Collection<? extends T> excludes) {
60
    public static <T> IncludeExclude<T> getNormalized(final Collection<? extends T> includes, final Collection<? extends T> excludes) {
55
        return new IncludeExclude<T>(includes, excludes).normal;
61
        return new IncludeExclude<T>(includes, excludes).normal;
56
    }
62
    }
57
 
63
 
58
    private final Set<T> includes;
64
    private final Set<T> includes;
59
    private final Set<T> excludes;
65
    private final Set<T> excludes;
60
    private final IncludeExclude<T> normal;
66
    private final IncludeExclude<T> normal;
61
 
67
 
62
    public IncludeExclude(final Collection<? extends T> includes) {
68
    public IncludeExclude(final Collection<? extends T> includes) {
63
        this(includes, Collections.<T> emptySet());
69
        this(includes, Collections.<T> emptySet());
64
    }
70
    }
65
 
71
 
66
    /**
72
    /**
67
     * Create a new instance.
73
     * Create a new instance.
68
     *
74
     *
69
     * @param includes which objects to include, <code>null</code> meaning all.
75
     * @param includes which objects to include, <code>null</code> meaning all.
70
     * @param excludes which objects to exclude, <code>null</code> meaning all.
76
     * @param excludes which objects to exclude, <code>null</code> meaning all.
71
     */
77
     */
72
    public IncludeExclude(final Collection<? extends T> includes, final Collection<? extends T> excludes) {
78
    public IncludeExclude(final Collection<? extends T> includes, final Collection<? extends T> excludes) {
73
        this(includes, excludes, false);
79
        this(includes, excludes, false);
74
    }
80
    }
75
 
81
 
76
    private IncludeExclude(final Collection<? extends T> includes, final Collection<? extends T> excludes, final boolean isNormal) {
82
    private IncludeExclude(final Collection<? extends T> includes, final Collection<? extends T> excludes, final boolean isNormal) {
77
        this(includes == null ? null : Collections.unmodifiableSet(new HashSet<T>(includes)), excludes == null ? null : Collections.unmodifiableSet(new HashSet<T>(excludes)), false, isNormal);
83
        this(includes == null ? null : Collections.unmodifiableSet(new HashSet<T>(includes)), excludes == null ? null : Collections.unmodifiableSet(new HashSet<T>(excludes)), false, isNormal);
78
    }
84
    }
79
 
85
 
80
    private IncludeExclude(final Set<T> includes, final Set<T> excludes, final boolean areMutable, final boolean isNormal) {
86
    private IncludeExclude(final Set<T> includes, final Set<T> excludes, final boolean areMutable, final boolean isNormal) {
81
        super();
87
        super();
82
        // parameter just to overload constructor
88
        // parameter just to overload constructor
83
        if (areMutable)
89
        if (areMutable)
84
            throw new IllegalStateException();
90
            throw new IllegalStateException();
85
        this.includes = includes;
91
        this.includes = includes;
86
        this.excludes = excludes;
92
        this.excludes = excludes;
87
        this.normal = isNormal ? this : this.normalize();
93
        this.normal = isNormal ? this : this.normalize();
88
        assert this.normal.excludes.isEmpty() || this.normal.includes == null;
94
        assert this.normal.excludes.isEmpty() || this.normal.includes == null;
89
    }
95
    }
90
 
96
 
91
    private final IncludeExclude<T> normalize() {
97
    private final IncludeExclude<T> normalize() {
92
        if (this.excludes == null || (this.includes != null && this.includes.isEmpty()))
98
        if (this.excludes == null || (this.includes != null && this.includes.isEmpty()))
93
            return getEmpty();
99
            return getEmpty();
94
        if (this.excludes.isEmpty() && this.includes == null)
100
        if (this.excludes.isEmpty() && this.includes == null)
95
            return getFull();
101
            return getFull();
96
        if (this.excludes.isEmpty() || this.includes == null)
102
        if (this.excludes.isEmpty() || this.includes == null)
97
            return this;
103
            return this;
98
        return new IncludeExclude<T>(CollectionUtils.substract(this.includes, this.excludes), Collections.<T> emptySet(), true);
104
        return new IncludeExclude<T>(CollectionUtils.substract(this.includes, this.excludes), Collections.<T> emptySet(), true);
99
    }
105
    }
100
 
106
 
101
    public final IncludeExclude<T> getNormal() {
107
    public final IncludeExclude<T> getNormal() {
102
        return this.normal;
108
        return this.normal;
103
    }
109
    }
104
 
110
 
105
    /**
111
    /**
106
     * Whether the passed object is included or excluded by this.
112
     * Whether the passed object is included or excluded by this.
107
     *
113
     *
108
     * @param s an object to test.
114
     * @param s an object to test.
109
     * @return <code>true</code> if is in include and not in exclude.
115
     * @return <code>true</code> if is in include and not in exclude.
110
     */
116
     */
111
    public final boolean isIncluded(final T s) {
117
    public final boolean isIncluded(final T s) {
-
 
118
        // "if" order is irrelevant since those methods use getNormal() and it can't be both "all"
-
 
119
        // and "none".
112
        if (this.isAllIncluded())
120
        if (this.isAllIncluded())
113
            return true;
121
            return true;
114
        else if (this.isNoneIncluded())
122
        else if (this.isNoneIncluded())
115
            return false;
123
            return false;
116
        else
124
        else
117
            return (this.normal.includes == null || this.normal.includes.contains(s)) && !this.normal.excludes.contains(s);
125
            return (this.normal.includes == null || this.normal.includes.contains(s)) && !this.normal.excludes.contains(s);
118
    }
126
    }
119
 
127
 
120
    public final <S extends T> Collection<S> getExcluded(final Collection<S> items) {
128
    public final <S extends T> Collection<S> getExcluded(final Collection<S> items) {
121
        if (this.areNoneIncluded(items))
129
        if (this.areNoneIncluded(items))
122
            return items;
130
            return items;
123
        else if (this.areAllIncluded(items))
131
        else if (this.areAllIncluded(items))
124
            return Collections.emptySet();
132
            return Collections.emptySet();
125
 
133
 
126
        final Set<S> res = new HashSet<>(items);
134
        final Set<S> res = new HashSet<>(items);
127
        // avoid checks of getIncluded()
135
        // avoid checks of getIncluded()
128
        res.removeAll(this.createIncluded(items));
136
        res.removeAll(this.createIncluded(items));
129
        return res;
137
        return res;
130
    }
138
    }
131
 
139
 
132
    public final <S extends T> Collection<S> getIncluded(final Collection<S> items) {
140
    public final <S extends T> Collection<S> getIncluded(final Collection<S> items) {
133
        if (this.areAllIncluded(items))
141
        if (this.areAllIncluded(items))
134
            return items;
142
            return items;
135
        else if (this.isNoneIncluded())
143
        else if (this.isNoneIncluded())
136
            return Collections.emptySet();
144
            return Collections.emptySet();
137
        else
145
        else
138
            return createIncluded(items);
146
            return createIncluded(items);
139
    }
147
    }
140
 
148
 
141
    private <S extends T> Collection<S> createIncluded(final Collection<S> items) {
149
    private <S extends T> Collection<S> createIncluded(final Collection<S> items) {
142
        final Set<S> res = new HashSet<>(items);
150
        final Set<S> res = new HashSet<>(items);
143
        if (this.normal.includes != null)
151
        if (this.normal.includes != null)
144
            res.retainAll(this.normal.includes);
152
            res.retainAll(this.normal.includes);
145
        res.removeAll(this.normal.excludes);
153
        res.removeAll(this.normal.excludes);
146
        return res;
154
        return res;
147
    }
155
    }
148
 
156
 
149
    public final boolean areAllIncluded(final Collection<? extends T> items) {
157
    public final boolean areAllIncluded(final Collection<? extends T> items) {
150
        if (this.isAllIncluded() || items.isEmpty())
158
        if (this.isAllIncluded() || items.isEmpty())
151
            return true;
159
            return true;
152
        else if (this.isNoneIncluded())
160
        else if (this.isNoneIncluded())
153
            return false;
161
            return false;
154
        else
162
        else
155
            return (this.normal.includes == null || this.normal.includes.containsAll(items)) && Collections.disjoint(this.normal.excludes, items);
163
            return (this.normal.includes == null || this.normal.includes.containsAll(items)) && Collections.disjoint(this.normal.excludes, items);
156
    }
164
    }
157
 
165
 
158
    public final boolean areNoneIncluded(final Collection<? extends T> items) {
166
    public final boolean areNoneIncluded(final Collection<? extends T> items) {
159
        if (this.isNoneIncluded() || items.isEmpty())
167
        if (this.isNoneIncluded() || items.isEmpty())
160
            return true;
168
            return true;
161
        else if (this.isAllIncluded())
169
        else if (this.isAllIncluded())
162
            return false;
170
            return false;
163
        else
171
        else
164
            return (this.normal.includes != null && Collections.disjoint(this.normal.includes, items)) || this.normal.excludes.containsAll(items);
172
            return (this.normal.includes != null && Collections.disjoint(this.normal.includes, items)) || this.normal.excludes.containsAll(items);
165
    }
173
    }
166
 
174
 
167
    /**
175
    /**
168
     * Whether this includes all objects.
176
     * Whether this includes all objects.
169
     *
177
     *
170
     * @return <code>true</code> if {@link #isIncluded(Object)} always return <code>true</code>
178
     * @return <code>true</code> if {@link #isIncluded(Object)} always return <code>true</code>
171
     */
179
     */
172
    public final boolean isAllIncluded() {
180
    public final boolean isAllIncluded() {
-
 
181
        // use normalized value to avoid ambiguous "include all" and "exclude all"
173
        return this.normal == FULL;
182
        return this.normal == FULL;
174
    }
183
    }
175
 
184
 
176
    /**
185
    /**
177
     * Whether this includes no objects.
186
     * Whether this includes no objects.
178
     *
187
     *
179
     * @return <code>true</code> if {@link #isIncluded(Object)} always return <code>false</code>
188
     * @return <code>true</code> if {@link #isIncluded(Object)} always return <code>false</code>
180
     */
189
     */
181
    public final boolean isNoneIncluded() {
190
    public final boolean isNoneIncluded() {
-
 
191
        // use normalized value to avoid ambiguous "include all" and "exclude all"
182
        return this.normal == EMPTY;
192
        return this.normal == EMPTY;
183
    }
193
    }
184
 
194
 
185
    /**
195
    /**
186
     * The one and only object included by this.
196
     * The one and only object included by this.
187
     *
197
     *
188
     * @return <code>true</code> if {@link #isIncluded(Object)} returns <code>true</code> for one
198
     * @return <code>true</code> if {@link #isIncluded(Object)} returns <code>true</code> for one
189
     *         and only one object.
199
     *         and only one object.
190
     */
200
     */
191
    public final Tuple2<Boolean, T> getSole() {
201
    public final Tuple2<Boolean, T> getSole() {
192
        if (this.normal.includes == null) {
202
        if (this.normal.includes == null) {
193
            return Tuple2.create(false, null);
203
            return Tuple2.create(false, null);
194
        } else {
204
        } else {
195
            assert this.normal.excludes.isEmpty();
205
            assert this.normal.excludes.isEmpty();
196
            final boolean res = this.normal.includes.size() == 1;
206
            final boolean res = this.normal.includes.size() == 1;
197
            return Tuple2.create(res, res ? this.normal.includes.iterator().next() : null);
207
            return Tuple2.create(res, res ? this.normal.includes.iterator().next() : null);
198
        }
208
        }
199
    }
209
    }
200
 
210
 
201
    /**
211
    /**
202
     * Return the one and only object included by this, or the passed object.
212
     * Return the one and only object included by this, or the passed object.
203
     *
213
     *
204
     * @param ifNoSole the object to return if this doesn't include exactly one object.
214
     * @param ifNoSole the object to return if this doesn't include exactly one object.
205
     * @return {@link #getSole()} or <code>ifNoSole</code>.
215
     * @return {@link #getSole()} or <code>ifNoSole</code>.
206
     */
216
     */
207
    public final T getSole(final T ifNoSole) {
217
    public final T getSole(final T ifNoSole) {
208
        final Tuple2<Boolean, T> sole = this.getSole();
218
        final Tuple2<Boolean, T> sole = this.getSole();
209
        if (sole.get0())
219
        if (sole.get0())
210
            return sole.get1();
220
            return sole.get1();
211
        else
221
        else
212
            return ifNoSole;
222
            return ifNoSole;
213
    }
223
    }
214
 
224
 
-
 
225
    @SafeVarargs
-
 
226
    public final IncludeExclude<T> include(final T... items) {
-
 
227
        return this.include(Arrays.asList(items));
-
 
228
    }
-
 
229
 
215
    public final IncludeExclude<T> include(final Collection<? extends T> items) {
230
    public final IncludeExclude<T> include(final Collection<? extends T> items) {
216
        if (this.areAllIncluded(items))
231
        if (this.areAllIncluded(items))
217
            return this;
232
            return this;
218
        else if (this.excludes == null)
233
        else if (this.excludes == null)
219
            throw new IllegalStateException("Cannot include an item when excluding all");
234
            throw new IllegalStateException("Cannot include an item when excluding all");
220
 
235
 
221
        Set<T> newIncludes;
236
        Set<T> newIncludes;
222
        if (this.includes == null || this.includes.containsAll(items)) {
237
        if (this.includes == null || this.includes.containsAll(items)) {
223
            newIncludes = this.includes;
238
            newIncludes = this.includes;
224
        } else {
239
        } else {
225
            newIncludes = new HashSet<>(this.includes);
240
            newIncludes = new HashSet<>(this.includes);
226
            newIncludes.addAll(items);
241
            newIncludes.addAll(items);
227
            newIncludes = Collections.unmodifiableSet(newIncludes);
242
            newIncludes = Collections.unmodifiableSet(newIncludes);
228
        }
243
        }
229
        Set<T> newExcludes;
244
        Set<T> newExcludes;
230
        if (Collections.disjoint(this.excludes, items)) {
245
        if (Collections.disjoint(this.excludes, items)) {
231
            newExcludes = this.excludes;
246
            newExcludes = this.excludes;
232
        } else {
247
        } else {
233
            newExcludes = new HashSet<>(this.excludes);
248
            newExcludes = new HashSet<>(this.excludes);
234
            newExcludes.removeAll(items);
249
            newExcludes.removeAll(items);
235
            newExcludes = Collections.unmodifiableSet(newExcludes);
250
            newExcludes = Collections.unmodifiableSet(newExcludes);
236
        }
251
        }
237
 
252
 
238
        return new IncludeExclude<T>(newIncludes, newExcludes, false, false);
253
        return new IncludeExclude<T>(newIncludes, newExcludes, false, false);
239
    }
254
    }
-
 
255
 
-
 
256
    @SafeVarargs
-
 
257
    public final IncludeExclude<T> exclude(final T... items) {
-
 
258
        return this.exclude(Arrays.asList(items));
-
 
259
    }
240
 
260
 
241
    public final IncludeExclude<T> exclude(final Collection<? extends T> items) {
261
    public final IncludeExclude<T> exclude(final Collection<? extends T> items) {
242
        if (this.areNoneIncluded(items))
262
        if (this.areNoneIncluded(items))
243
            return this;
263
            return this;
244
 
264
 
245
        assert this.excludes != null : "!areNoneIncluded() but this.excludes == null";
265
        assert this.excludes != null : "!areNoneIncluded() but this.excludes == null";
246
        Set<T> newExcludes = new HashSet<>(this.excludes);
266
        Set<T> newExcludes = new HashSet<>(this.excludes);
247
        newExcludes.addAll(items);
267
        newExcludes.addAll(items);
248
        newExcludes = Collections.unmodifiableSet(newExcludes);
268
        newExcludes = Collections.unmodifiableSet(newExcludes);
249
        return new IncludeExclude<T>(this.includes, Collections.unmodifiableSet(newExcludes), false, false);
269
        return new IncludeExclude<T>(this.includes, Collections.unmodifiableSet(newExcludes), false, false);
250
    }
270
    }
251
 
271
 
252
    @Override
272
    @Override
253
    public int hashCode() {
273
    public int hashCode() {
254
        final int prime = 31;
274
        final int prime = 31;
255
        int result = 1;
275
        int result = 1;
256
        result = prime * result + ((this.excludes == null) ? 0 : this.excludes.hashCode());
276
        result = prime * result + ((this.excludes == null) ? 0 : this.excludes.hashCode());
257
        result = prime * result + ((this.includes == null) ? 0 : this.includes.hashCode());
277
        result = prime * result + ((this.includes == null) ? 0 : this.includes.hashCode());
258
        return result;
278
        return result;
259
    }
279
    }
260
 
280
 
261
    @Override
281
    @Override
262
    public boolean equals(final Object obj) {
282
    public boolean equals(final Object obj) {
263
        if (this == obj)
283
        if (this == obj)
264
            return true;
284
            return true;
265
        if (obj == null)
285
        if (obj == null)
266
            return false;
286
            return false;
267
        if (getClass() != obj.getClass())
287
        if (getClass() != obj.getClass())
268
            return false;
288
            return false;
269
        final IncludeExclude<?> other = (IncludeExclude<?>) obj;
289
        final IncludeExclude<?> other = (IncludeExclude<?>) obj;
270
        return CompareUtils.equals(this.includes, other.includes) && CompareUtils.equals(this.excludes, other.excludes);
290
        return CompareUtils.equals(this.includes, other.includes) && CompareUtils.equals(this.excludes, other.excludes);
271
    }
291
    }
272
 
292
 
273
    @Override
293
    @Override
274
    public String toString() {
294
    public String toString() {
275
        final String suffix;
295
        final String suffix;
276
        if (this == FULL)
296
        if (this == FULL)
277
            suffix = " ALL";
297
            suffix = " ALL";
278
        else if (this == EMPTY)
298
        else if (this == EMPTY)
279
            suffix = " NONE";
299
            suffix = " NONE";
280
        else
300
        else
281
            suffix = " includes " + this.includes + " except " + this.excludes;
301
            suffix = " includes " + this.includes + " except " + this.excludes;
282
        return this.getClass().getSimpleName() + suffix;
302
        return this.getClass().getSimpleName() + suffix;
283
    }
303
    }
284
}
304
}