OpenConcerto

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

svn://code.openconcerto.org/openconcerto

Rev

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