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     */
017    package org.apache.commons.cli2.util;
018    
019    import java.util.Comparator;
020    import java.util.List;
021    
022    import org.apache.commons.cli2.Group;
023    import org.apache.commons.cli2.Option;
024    import org.apache.commons.cli2.option.Command;
025    import org.apache.commons.cli2.option.DefaultOption;
026    import org.apache.commons.cli2.option.Switch;
027    
028    /**
029     * A collection of Comparators suitable for use with Option instances.
030     */
031    public 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    }