summaryrefslogtreecommitdiffstats
blob: 6fb73cbe51b23de50b501149f15b634f732de9b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/*
 * Copyright (c) 2010-2016, Isode Limited, London, England.
 * All rights reserved.
 */
/*
 * Copyright (c) 2010, Remko Tronçon.
 * All rights reserved.
 */
package com.isode.stroke.network;

import com.ibm.icu.util.BytesTrie.Iterator;
import com.isode.stroke.base.RandomGenerator;
import com.isode.stroke.signals.Signal1;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public abstract class DomainNameServiceQuery {

    public static class Result {

        public Result() {
            hostname = "";
            port = -1;
            priority = -1;
            weight = -1;
        }

        public Result(String hostname, int port, int priority, int weight) {
            this.hostname = hostname;
            this.port = port;
            this.priority = priority;
            this.weight = weight;
        }
        public final String hostname;
        public final int port;
        public final int priority;
        public final int weight;
    };

    /**
     * Compares {@link DomainNameServiceQuery.Result} based on their 
     * priority only.
     *
     */
    public final static class ResultPriorityComparator implements Comparator<Result> {

        public int compare(DomainNameServiceQuery.Result a, DomainNameServiceQuery.Result b) {
            if (a.priority < b.priority) {
                return -1;
            }
            if (a.priority > b.priority) {
                return 1;
            }
            return 0;
        }
    };

    public abstract void run();
    public final Signal1<Collection<Result>> onResult = new Signal1<Collection<Result>>();
    
    public static void sortResults(List<Result> queries,RandomGenerator generator) {
        ResultPriorityComparator comparator = new ResultPriorityComparator();
        Collections.sort(queries,comparator);
        int i = 0;
        while (i < queries.size()) {
            Result current = queries.get(i);
            int nextI = upperBound(queries, current, comparator);
            if ((nextI - i) > 1) {
                List<Result> subList = queries.subList(i, nextI);
                List<Integer> weights = transform(subList, new UnaryOperation<Result, Integer>() {

                    @Override
                    public Integer perform(Result item) {
                        // easy hack to account for '0' weights getting at least some weight
                        return Integer.valueOf(item.weight+1);
                    }

                });
                for (int j = 0; j < (weights.size()-1); j++) {
                    List<Integer> cumulativeWeights =
                            partialSum(weights.subList(j, weights.size()), Integer.valueOf(0), 
                                    new BinaryOperation<Integer, Integer, Integer>() {

                                @Override
                                public Integer perform(Integer int1,
                                        Integer int2) {
                                    int results = int1.intValue() + int2.intValue();
                                    return Integer.valueOf(results);
                                }
                                
                            });
                    int lastWeight = cumulativeWeights.get(cumulativeWeights.size() - 1).intValue();
                    int randomNumber = generator.generateRandomInteger(lastWeight);
                    int selectedIndex = lowerBound(cumulativeWeights, Integer.valueOf(randomNumber));
                    Collections.swap(subList, j, j+selectedIndex);
                    Collections.swap(weights, j, j+selectedIndex);
                }
                
            }
            i = nextI;
        }
    }
    
    /**
     * Behaves similarly to the c++ function std::upper_bound.
     * Given a range that is sorted with respect to the given
     * {@link Comparator}, returns the index of the first element in the range greater
     * (under the {@link Comparator}) then the given value.
     * @param <T> The type of the value.
     * @param range A sorted (with respect to {@code comp}) list of
     * T.  Should not be {@code null}.
     * @param value A value to search the list for.  Should not be {@code null}.
     * @param comp The comparator to use for the search, the {@code range} should be
     * sorted with respect to this.  Should not be {@code null}.
     * @return The index of the first element in {@code range} that is greater (under
     * {@code comp}) then {@code value}, or {@code range.size()} if there is no such value.
     */
    public static <T> int upperBound(List<? extends T> range,T value,Comparator<? super T> comp) {
        for (int i = 0; i < range.size(); i++) {
            T current = range.get(i);
            if (comp.compare(current, value) > 0) {
                return i;
            }
        }
        return range.size();
    }
    
    /**
     * Behaves similarly to the c++ function std::upper_bound.
     * Given a range that is sorted with respect to the natural order of its elements
     * , returns the index of the first element in the range greater then the given value.
     * @param <T> The type of the value.
     * @param range A sorted list of
     * T.  Should not be {@code null}.
     * @param value A value to search the list for.  Should not be {@code null}.
     * @return The index of the first element in {@code range} that is greate then {@code value},
     * or {@code range.size()} if there is no such value.
     */
    public static <T extends Comparable<T>> int upperBound(List<? extends T> range,T value) {
        Comparator<T> comparator = new Comparator<T>() {

            @Override
            public int compare(T a, T b) {
                return a.compareTo(b);
            }
        };
        return upperBound(range, value, comparator);
    }
    
    /**
     * Behaves similarly to the c++ function std::lower_bound.
     * Given a range that is sorted with respect to the given
     * {@link Comparator}, returns the index of the first element in the range that is not
     * less then (i.e it is greater then or equal to) a given value.
     * @param <T> The type of the value.
     * @param range A sorted (with respect to {@code comp}) list of
     * T.  Should not be {@code null}.
     * @param value A value to search the list for.  Should not be {@code null}.
     * @param comp The comparator to use for the search, the {@code range} should be
     * sorted with respect to this.  Should not be {@code null}.
     * @return The index of the first element in {@code range} that is not less then 
     * (i.e it is greater then or equal to) then {@code value}, or {@code range.size()} 
     * if there is no such value.
     */
    public static <T> int lowerBound(List<? extends T> range,T value,Comparator<? super T> comp) {
        for (int i = 0; i < range.size(); i++) {
            T current = range.get(i);
            if (comp.compare(current, value) < 0) {
                continue;
            }
            return i;
        }
        return range.size();
    }
    
    /**
     * Behaves similarly to the c++ function std::lower_bound.
     * Given a range that is sorted with respect to the natural order of its elements
     * , returns the index of the first element in the range that is not less then
     * (i.e it is greater then or equal to) a given value.
     * @param <T> The type of the value.
     * @param range A sorted list of
     * T.  Should not be {@code null}.
     * @param value A value to search the list for.  Should not be {@code null}.
     * @return The index of the first element in {@code range} that is not less then
     * (i.e it is greater then or equal to) {@code value},
     * or {@code range.size()} if there is no such value.
     */
    public static <T extends Comparable<T>> int lowerBound(List<? extends T> range,T value) {
        Comparator<T> comparator = new Comparator<T>() {

            @Override
            public int compare(T a, T b) {
                return a.compareTo(b);
            }
        };
        return lowerBound(range, value, comparator);
    }
    
    /**
     * Operation that produces an item of type T from
     * an item of type S
     * @param <S> Type of input to the operation
     * @param <T> Type of output of the operation
     */
    public interface UnaryOperation<S,T> {
        
        /**
         * Produces an item of type T from an item of type S
         * @param item An item of type S.
         * @return An item of type T
         */
        public T perform(S item);
        
    }
    
    /**
     * Operation that produces an item of type T from
     * an items of type R and an item of type S.
     *
     * @param <R> Type of the first input to the operation.
     * @param <S> Type of the second input to the operation.
     * @param <T> Type of the output of the operation.
     */
    public interface BinaryOperation<R,S,T> {
        
        /**
         * Produces an item of type from an Item of type R
         * and an Item of type S
         * @param input1 An item of type R
         * @param input2 An item of type S
         * @return An item of type T
         */
        public T perform(R input1,S input2);
        
    }
    
    /**
     * Behaves similar to the C++ function std::transform
     * Transform a list of items of type T into a list of item of
     * type S using the given {@link UnaryOperation}
     * @param <S> Type of the input objects
     * @param <T> Type of the output objects.
     * @param input The input objects to apply the {@link UnaryOperation}
     * to.
     * @param operation The {@link UnaryOperation} to apply to the input
     * to get the output.
     * @return The results of applying the {@link UnaryOperation} to
     * the input in the order they are returned by the input's {@link Iterator}.
     */
    public static <S,T> List<T> transform(Collection<? extends S> input,
            UnaryOperation<? super S,? extends T> operation) {
        List<T> output = new ArrayList<T>(input.size());
        for (S item : input) {
            T product = operation.perform(item);
            output.add(product);
        }
        return output;
    }
    
    /**
     * Produces the partial sum of the elements in a {@link Collections} using the
     * given {@link BinaryOperation}.
     * @param <T> The type of objects to calculate the sum for
     * @param input The items to produce the partial sums for.  Should not be {@code null}.
     * @param initialValue The initial value for the sum to add the first element.
     * @param sumOp The {@link BinaryOperation} to produce the sum must take two items
     * of type T to produce another item of type T.  Should not be {@code null}.
     * @return A {@link List} of T consisting of the partial sums of the input. 
     * Will not be {@code null}
     */
    public static <T> List<T> partialSum(Collection<? extends T> input,
            T initialValue,BinaryOperation<? super T,? super T,? extends T> sumOp) {
        List<T> partialSums = new ArrayList<T>(input.size());
        T previousSum = initialValue;
        for (T currentInput : input) {
            T newSum = sumOp.perform(previousSum, currentInput);
            partialSums.add(newSum);
            previousSum = newSum;
        }
        return partialSums;
    }
    
}