001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.cli2.util;
018
019import java.util.Comparator;
020import java.util.List;
021
022import org.apache.commons.cli2.Group;
023import org.apache.commons.cli2.Option;
024import org.apache.commons.cli2.option.Command;
025import org.apache.commons.cli2.option.DefaultOption;
026import org.apache.commons.cli2.option.Switch;
027
028/**
029 * A collection of Comparators suitable for use with Option instances.
030 */
031public class Comparators {
032
033    private Comparators(){
034        // constructor hiden from potential users
035    }
036
037
038    /**
039     * Chains comparators together.
040     *
041     * @see #chain(Comparator[])
042     * @param c0
043     *            a comparator
044     * @param c1
045     *            a comparator
046     * @return a chained comparator
047     */
048    public static Comparator chain(final Comparator c0, final Comparator c1) {
049        return chain(new Comparator[] { c0, c1 });
050    }
051
052    /**
053     * Chains comparators together.
054     *
055     * @see #chain(Comparator[])
056     * @param c0
057     *            a comparator
058     * @param c1
059     *            a comparator
060     * @param c2
061     *            a comparator
062     * @return a chained comparator
063     */
064    public static Comparator chain(
065        final Comparator c0,
066        final Comparator c1,
067        final Comparator c2) {
068        return chain(new Comparator[] { c0, c1, c2 });
069    }
070
071    /**
072     * Chains comparators together.
073     *
074     * @see #chain(Comparator[])
075     * @param c0
076     *            a comparator
077     * @param c1
078     *            a comparator
079     * @param c2
080     *            a comparator
081     * @param c3
082     *            a comparator
083     * @return a chained comparator
084     */
085    public static Comparator chain(
086        final Comparator c0,
087        final Comparator c1,
088        final Comparator c2,
089        final Comparator c3) {
090        return chain(new Comparator[] { c0, c1, c2, c3 });
091    }
092
093    /**
094     * Chains comparators together.
095     *
096     * @see #chain(Comparator[])
097     * @param c0
098     *            a comparator
099     * @param c1
100     *            a comparator
101     * @param c2
102     *            a comparator
103     * @param c3
104     *            a comparator
105     * @param c4
106     *            a comparator
107     * @return a chained comparator
108     */
109    public static Comparator chain(
110        final Comparator c0,
111        final Comparator c1,
112        final Comparator c2,
113        final Comparator c3,
114        final Comparator c4) {
115        return chain(new Comparator[] { c0, c1, c2, c3, c4 });
116    }
117
118    /**
119     * Chains comparators together.
120     *
121     * @see #chain(Comparator[])
122     * @param comparators
123     *            a List of comparators to chain together
124     * @return a chained comparator
125     */
126    public static Comparator chain(final List comparators) {
127        return new Chain(
128            (Comparator[])comparators.toArray(
129                new Comparator[comparators.size()]));
130    }
131
132    /**
133     * Chains an array of comparators together. Each Comparator will be called
134     * in turn until one of them return a non-zero value, this value will be
135     * returned.
136     *
137     * @param comparators
138     *            the array of comparators
139     * @return a chained comparator
140     */
141    public static Comparator chain(final Comparator[] comparators) {
142        return new Chain(comparators);
143    }
144
145    /**
146     * Chains a series of Comparators together.
147     */
148    private static class Chain implements Comparator {
149
150        final Comparator[] chain;
151
152        /**
153         * Creates a Comparator chain using the specified array of Comparators
154         * @param chain the Comparators in the chain
155         */
156        public Chain(final Comparator[] chain) {
157            this.chain = new Comparator[chain.length];
158            System.arraycopy(chain, 0, this.chain, 0, chain.length);
159        }
160
161        public int compare(final Object left, final Object right) {
162            int result = 0;
163            for (int i = 0; result == 0 && i < chain.length; ++i) {
164                result = chain[i].compare(left, right);
165            }
166            return result;
167        }
168    }
169
170    /**
171     * Reverses a comparator's logic.
172     *
173     * @param wrapped
174     *            the Comparator to reverse the logic of
175     * @return a comparator with reverse logic
176     */
177    private static Comparator reverse(final Comparator wrapped) {
178        return new Reverse(wrapped);
179    }
180
181    private static class Reverse implements Comparator {
182        private final Comparator wrapped;
183
184        /**
185         * Creates a Comparator with reverse logic
186         * @param wrapped the original logic
187         */
188        public Reverse(final Comparator wrapped) {
189            this.wrapped = wrapped;
190        }
191
192        public int compare(final Object left, final Object right) {
193            return -wrapped.compare(left, right);
194        }
195    }
196
197    /**
198     * Forces Group instances to appear at the beginning of lists
199     *
200     * @see Group
201     * @return a new comparator
202     */
203    public static Comparator groupFirst() {
204        return new GroupFirst();
205    }
206
207    /**
208     * Forces Group instances to appear at the end of lists
209     *
210     * @see Group
211     * @return a new comparator
212     */
213    public static Comparator groupLast() {
214        return reverse(groupFirst());
215    }
216
217    private static class GroupFirst implements Comparator {
218        public int compare(final Object left, final Object right) {
219            final boolean l = left instanceof Group;
220            final boolean r = right instanceof Group;
221
222            if (l ^ r) {
223                if (l) {
224                    return -1;
225                }
226                return 1;
227            }
228            return 0;
229        }
230    }
231
232    /**
233     * Forces Switch instances to appear at the beginning of lists
234     *
235     * @see Switch
236     * @return a new comparator
237     */
238    public static Comparator switchFirst() {
239        return new SwitchFirst();
240    }
241
242    /**
243     * Forces Switch instances to appear at the end of lists
244     *
245     * @see Switch
246     * @return a new comparator
247     */
248    public static Comparator switchLast() {
249        return reverse(switchFirst());
250    }
251
252    private static class SwitchFirst implements Comparator {
253        public int compare(final Object left, final Object right) {
254            final boolean l = left instanceof Switch;
255            final boolean r = right instanceof Switch;
256
257            if (l ^ r) {
258                if (l) {
259                    return -1;
260                }
261                return 1;
262            }
263            return 0;
264        }
265    }
266
267    /**
268     * Forces Command instances to appear at the beginning of lists
269     *
270     * @see Command
271     * @return a new comparator
272     */
273    public static Comparator commandFirst() {
274        return new CommandFirst();
275    }
276
277    /**
278     * Forces Command instances to appear at the end of lists
279     *
280     * @see Command
281     * @return a new comparator
282     */
283    public static Comparator commandLast() {
284        return reverse(commandFirst());
285    }
286
287    private static class CommandFirst implements Comparator {
288        public int compare(final Object left, final Object right) {
289            final boolean l = left instanceof Command;
290            final boolean r = right instanceof Command;
291
292            if (l ^ r) {
293                if (l) {
294                    return -1;
295                }
296                return 1;
297            }
298            return 0;
299        }
300    }
301
302    /**
303     * Forces DefaultOption instances to appear at the beginning of lists
304     *
305     * @see DefaultOption
306     * @return a new comparator
307     */
308    public static Comparator defaultOptionFirst() {
309        return new DefaultOptionFirst();
310    }
311
312    /**
313     * Forces DefaultOption instances to appear at the end of lists
314     *
315     * @see DefaultOption
316     * @return a new comparator
317     */
318    public static Comparator defaultOptionLast() {
319        return reverse(defaultOptionFirst());
320    }
321
322    private static class DefaultOptionFirst implements Comparator {
323        public int compare(final Object left, final Object right) {
324            final boolean l = left instanceof DefaultOption;
325            final boolean r = right instanceof DefaultOption;
326
327            if (l ^ r) {
328                if (l) {
329                    return -1;
330                }
331                return 1;
332            }
333            return 0;
334        }
335    }
336
337    /**
338     * Forces Comparators with a particular trigger to appear at the beginning
339     * of lists
340     *
341     * @param name
342     *            the trigger name to select
343     * @see Option#getTriggers()
344     * @return a new comparator
345     */
346    public static Comparator namedFirst(final String name) {
347        return new Named(name);
348    }
349
350    /**
351     * Forces Comparators with a particular trigger to appear at the end of
352     * lists
353     *
354     * @param name
355     *            the trigger name to select
356     * @see Option#getTriggers()
357     * @return a new comparator
358     */
359    public static Comparator namedLast(final String name) {
360        return reverse(new Named(name));
361    }
362
363    private static class Named implements Comparator {
364        private final String name;
365
366        /**
367         * Creates a Comparator that sorts a particular name high in order
368         * @param name the trigger name to select
369         */
370        public Named(final String name) {
371            this.name = name;
372        }
373        public int compare(final Object oleft, final Object oright) {
374            final Option left = (Option)oleft;
375            final Option right = (Option)oright;
376
377            final boolean l = left.getTriggers().contains(name);
378            final boolean r = right.getTriggers().contains(name);
379
380            if (l ^ r) {
381                if (l) {
382                    return -1;
383                }
384                return 1;
385            }
386            return 0;
387        }
388    }
389
390    /**
391     * Orders Options by preferredName
392     *
393     * @see Option#getPreferredName()
394     * @return a new comparator
395     */
396    public static Comparator preferredNameFirst() {
397        return new PreferredName();
398    }
399
400    /**
401     * Orders Options by preferredName, reversed
402     *
403     * @see Option#getPreferredName()
404     * @return a new comparator
405     */
406    public static Comparator preferredNameLast() {
407        return reverse(preferredNameFirst());
408    }
409
410    private static class PreferredName implements Comparator {
411        public int compare(final Object oleft, final Object oright) {
412            final Option left = (Option)oleft;
413            final Option right = (Option)oright;
414
415            return left.getPreferredName().compareTo(right.getPreferredName());
416        }
417    }
418
419    /**
420     * Orders Options grouping required Options first
421     *
422     * @see Option#isRequired()
423     * @return a new comparator
424     */
425    public static Comparator requiredFirst() {
426        return new Required();
427    }
428
429    /**
430     * Orders Options grouping required Options last
431     *
432     * @see Option#isRequired()
433     * @return a new comparator
434     */
435    public static Comparator requiredLast() {
436        return reverse(requiredFirst());
437    }
438
439    private static class Required implements Comparator {
440        public int compare(final Object oleft, final Object oright) {
441            final Option left = (Option)oleft;
442            final Option right = (Option)oright;
443
444            final boolean l = left.isRequired();
445            final boolean r = right.isRequired();
446
447            if (l ^ r) {
448                if (l) {
449                    return -1;
450                }
451                return 1;
452            }
453            return 0;
454        }
455    }
456}