OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

Rev 144 | Rev 182 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 ilm 1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
5
 *
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
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.
10
 *
11
 * When distributing the software, include this License Header Notice in each file.
12
 */
13
 
14
 package org.openconcerto.utils.cc;
15
 
16
import org.openconcerto.utils.CompareUtils;
17
 
18
import java.util.ArrayList;
19
import java.util.Collection;
20
import java.util.LinkedHashSet;
21
import java.util.List;
174 ilm 22
import java.util.ListIterator;
23
import java.util.RandomAccess;
94 ilm 24
import java.util.Set;
174 ilm 25
import java.util.function.BiPredicate;
94 ilm 26
 
27
/**
28
 * Allow to create a proxy object which uses a {@link HashingStrategy}.
29
 *
30
 * @author Sylvain
31
 */
32
public class CustomEquals {
33
 
34
    static private final HashingStrategy<Object> DEFAULT = new HashingStrategy<Object>() {
35
        @Override
36
        public boolean equals(Object object1, Object object2) {
37
            return CompareUtils.equals(object1, object2);
38
        }
39
 
40
        @Override
41
        public int computeHashCode(Object object) {
42
            return object == null ? 0 : object.hashCode();
43
        }
44
    };
45
 
46
    static private final HashingStrategy<Object> IDENTITY = new HashingStrategy<Object>() {
47
        @Override
48
        public boolean equals(Object object1, Object object2) {
49
            return object1 == object2;
50
        }
51
 
52
        @Override
53
        public int computeHashCode(Object object) {
54
            return System.identityHashCode(object);
55
        }
56
    };
57
 
58
    static public HashingStrategy<Object> getDefault() {
59
        return DEFAULT;
60
    }
61
 
62
    static public HashingStrategy<Object> getIdentity() {
63
        return IDENTITY;
64
    }
65
 
66
    // only create sets if necessary
144 ilm 67
    static public final <S, T1 extends S, T2 extends S> boolean equals(final Set<T1> s1, final Set<T2> s2, final HashingStrategy<S> s) {
94 ilm 68
        return equals(s1, s2, s, true);
69
    }
70
 
144 ilm 71
    static public final <S, T1 extends S, T2 extends S> boolean equals(final List<T1> s1, final List<T2> s2, final HashingStrategy<S> s) {
94 ilm 72
        return equals(s1, s2, s, false);
73
    }
74
 
75
    /**
76
     * Test equality of 2 collections using the passed strategy.
77
     *
144 ilm 78
     * @param <S> type of strategy.
79
     * @param <T1> type of first collection.
80
     * @param <T2> type of second collection.
94 ilm 81
     * @param s1 the first collection.
82
     * @param s2 the second collection.
83
     * @param s the hashing strategy, <code>null</code> meaning the {@link #getDefault() default}.
84
     * @param set <code>true</code> if the passed collections should be used as a {@link Set},
85
     *        <code>false</code> for a {@link List}.
86
     * @return <code>true</code> if the 2 collections are equal using the passed parameters.
87
     */
144 ilm 88
    static public final <S, T1 extends S, T2 extends S> boolean equals(final Collection<T1> s1, final Collection<T2> s2, final HashingStrategy<S> s, final boolean set) {
94 ilm 89
        final Collection<?> sA, sB;
90
        // if the caller ask that the collections are compared using Set or List make sure that they
91
        // already are.
92
        final Class<?> clazz = set ? Set.class : List.class;
93
        if ((s == null || s == DEFAULT) && clazz.isInstance(s1) && clazz.isInstance(s2)) {
94
            sA = s1;
95
            sB = s2;
96
            // boolean to only create collections if necessary (don't make callers create the empty
97
            // collections)
98
        } else if (set) {
99
            sA = ProxyFull.createSet(s, s1);
100
            sB = ProxyFull.createSet(s, s2);
101
        } else {
102
            sA = ProxyFull.createList(s, s1);
103
            sB = ProxyFull.createList(s, s2);
104
        }
105
        assert clazz.isInstance(sA) && clazz.isInstance(sB);
106
        return CompareUtils.equals(sA, sB);
107
    }
108
 
174 ilm 109
    static public final <S, T1 extends S> int indexOf(final List<T1> s1, final S o, final HashingStrategy<S> s) {
110
        return indexOf(s1, o, s::equals);
111
    }
112
 
113
    static public final <S, T1 extends S> int indexOf(final List<T1> s1, final S o, final BiPredicate<S, S> s) {
114
        if (s1 instanceof RandomAccess) {
115
            final int stop = s1.size();
116
            for (int i = 0; i < stop; i++) {
117
                if (s.test(s1.get(i), o))
118
                    return i;
119
            }
120
        } else {
121
            final ListIterator<T1> listIter = s1.listIterator();
122
            while (listIter.hasNext()) {
123
                final T1 item = listIter.next();
124
                if (s.test(item, o))
125
                    return listIter.previousIndex();
126
            }
127
        }
128
        return -1;
129
    }
130
 
132 ilm 131
    static public class ProxyFull<S, E extends S> implements ProxyItf<E> {
94 ilm 132
 
133
        static public final <S, E extends S> Set<ProxyFull<S, E>> createSet(final HashingStrategy<S> strategy, final Collection<E> coll) {
134
            return wrap(strategy, coll, new LinkedHashSet<ProxyFull<S, E>>());
135
        }
136
 
137
        static public final <S, E extends S> List<ProxyFull<S, E>> createList(final HashingStrategy<S> strategy, final Collection<E> coll) {
138
            return wrap(strategy, coll, new ArrayList<ProxyFull<S, E>>());
139
        }
140
 
141
        static public final <S, E extends S, C extends Collection<? super ProxyFull<S, E>>> C wrap(final HashingStrategy<S> strategy, final Collection<E> coll, final C res) {
142
            for (final E item : coll) {
143
                res.add(new ProxyFull<S, E>(item, strategy));
144
            }
145
            return res;
146
        }
147
 
148
        private final E delegate;
149
        private final HashingStrategy<S> strategy;
150
 
151
        /**
152
         * Create a proxy object that use a strategy to implement {@link #equals(Object)} and
153
         * {@link #hashCode()}.
154
         *
155
         * @param delegate the object to use.
156
         * @param strategy the strategy, <code>null</code> meaning use default implementation.
157
         */
158
        public ProxyFull(final E delegate, final HashingStrategy<S> strategy) {
159
            this.delegate = delegate;
160
            /**
161
             * Allow null strategy since the caller doesn't have to deal with generic limitations :
162
             *
163
             * <pre>
164
             * HashingStrategy&lt;Constraint&gt; strategy = sameSystem ? null : Constraint.getInterSystemHashStrategy();
165
             * </pre>
166
             *
167
             * Otherwise if one have HashingStrategy&lt;Object&gt; and
168
             * HashingStrategy&lt;Constraint&gt; ProxyFull.createSet() can't be called since no Java
169
             * type can hold both those instances. In this class the two cases are handled
170
             * explicitly.
171
             */
172
            this.strategy = strategy;
173
        }
174
 
132 ilm 175
        @Override
94 ilm 176
        public final E getDelegate() {
177
            return this.delegate;
178
        }
179
 
132 ilm 180
        @Override
94 ilm 181
        public final HashingStrategy<S> getStrategy() {
182
            return this.strategy;
183
        }
184
 
185
        private final HashingStrategy<?> getInternalStrategy() {
186
            return this.strategy != null ? this.strategy : DEFAULT;
187
        }
188
 
189
        @Override
190
        public int hashCode() {
191
            // don't use getInternalStrategy() to avoid java warning
192
            if (this.strategy == null)
193
                return DEFAULT.computeHashCode(this.getDelegate());
194
            else
195
                return this.getStrategy().computeHashCode(this.getDelegate());
196
        }
197
 
198
        @Override
199
        public boolean equals(Object obj) {
200
            if (this == obj)
201
                return true;
202
            if (!(obj instanceof ProxyFull))
203
                return false;
204
            final ProxyFull<?, ?> other = (ProxyFull<?, ?>) obj;
205
            // null and DEFAULT mean the same
206
            if (other.getInternalStrategy() != this.getInternalStrategy())
207
                return false;
208
            // OK since same instance of strategy
209
            @SuppressWarnings("unchecked")
210
            final S delegate2 = (S) other.getDelegate();
211
            final E delegate = this.getDelegate();
212
            if (delegate == delegate2)
213
                return true;
214
            // don't use getInternalStrategy() to avoid java warning
215
            if (this.strategy == null)
216
                return DEFAULT.equals(this.getDelegate(), delegate2);
217
            else
218
                return this.strategy.equals(this.getDelegate(), delegate2);
219
        }
174 ilm 220
 
221
        @Override
222
        public String toString() {
223
            return this.getClass().getSimpleName() + " using strategy " + this.getStrategy() + " for " + this.getDelegate();
224
        }
94 ilm 225
    }
226
 
227
    /**
132 ilm 228
     * A proxy object which uses a {@link HashingStrategy}.
94 ilm 229
     *
230
     * @author Sylvain
231
     * @param <E> type of item
232
     */
132 ilm 233
    static public interface ProxyItf<E> {
234
        public E getDelegate();
94 ilm 235
 
132 ilm 236
        public HashingStrategy<? super E> getStrategy();
237
    }
94 ilm 238
 
132 ilm 239
    static public final <S, E extends S> Set<ProxyItf<E>> createSet(final HashingStrategy<S> strategy, final Collection<E> coll) {
240
        return ProxyFull.wrap(strategy, coll, new LinkedHashSet<ProxyItf<E>>());
241
    }
94 ilm 242
 
132 ilm 243
    static public final <S, E extends S> List<ProxyItf<E>> createList(final HashingStrategy<S> strategy, final Collection<E> coll) {
244
        return ProxyFull.wrap(strategy, coll, new ArrayList<ProxyItf<E>>());
245
    }
246
 
247
    static public final <T> Set<T> unwrapToSet(final Collection<? extends ProxyItf<T>> coll) {
248
        return unwrap(coll, new LinkedHashSet<T>());
249
    }
250
 
251
    static public final <T> List<T> unwrapToList(final Collection<? extends ProxyItf<T>> coll) {
252
        return unwrap(coll, new ArrayList<T>());
253
    }
254
 
255
    static public final <T, C extends Collection<? super T>> C unwrap(final Collection<? extends ProxyItf<T>> coll, final C res) {
256
        for (final ProxyItf<T> item : coll) {
257
            res.add(item.getDelegate());
94 ilm 258
        }
132 ilm 259
        return res;
260
    }
94 ilm 261
 
132 ilm 262
    /**
263
     * A simple subclass to save some typing and improve legibility.
264
     *
265
     * @author Sylvain
266
     * @param <E> type of item
267
     */
268
    static public class Proxy<E> extends ProxyFull<E, E> {
269
 
94 ilm 270
        public Proxy(E delegate, HashingStrategy<E> strategy) {
271
            super(delegate, strategy);
272
        }
273
    }
274
 
275
    private CustomEquals() {
276
    }
277
}