001    /**
002     * Copyright 2003-2004 The Apache Software Foundation
003     * Copyright 2005 Stephen McConnell
004     *
005     * Licensed under the Apache License, Version 2.0 (the "License");
006     * you may not use this file except in compliance with the License.
007     * 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 net.dpml.cli.util;
018    
019    import java.util.Comparator;
020    import java.util.List;
021    
022    import net.dpml.cli.Group;
023    import net.dpml.cli.Option;
024    import net.dpml.cli.option.Command;
025    import net.dpml.cli.option.DefaultOption;
026    import net.dpml.cli.option.Switch;
027    
028    /**
029     * A collection of Comparators suitable for use with Option instances.
030     * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
031     * @version 1.0.0
032     */
033    public final class Comparators 
034    {
035        private Comparators()
036        {
037            // static
038        }
039        
040        /**
041         * Chains comparators together.
042         * 
043         * @see #chain(Comparator[])
044         * @param c0 a comparator
045         * @param c1 a comparator
046         * @return a chained comparator
047         */
048        public static Comparator chain( final Comparator c0, final Comparator c1 )
049        {
050            return chain( new Comparator[]{c0, c1} );
051        }
052    
053        /**
054         * Chains comparators together.
055         * 
056         * @see #chain(Comparator[])
057         * @param c0 a comparator
058         * @param c1 a comparator
059         * @param c2 a comparator
060         * @return a chained comparator
061         */
062        public static Comparator chain( final Comparator c0, final Comparator c1, final Comparator c2 )
063        {
064            return chain( new Comparator[]{c0, c1, c2} );
065        }
066    
067        /**
068         * Chains comparators together.
069         * 
070         * @see #chain(Comparator[])
071         * @param c0 a comparator
072         * @param c1 a comparator
073         * @param c2 a comparator
074         * @param c3 a comparator
075         * @return a chained comparator
076         */
077        public static Comparator chain( 
078          final Comparator c0, final Comparator c1, final Comparator c2, final Comparator c3 )
079        {
080            return chain( new Comparator[]{c0, c1, c2, c3} );
081        }
082    
083        /**
084         * Chains comparators together.
085         * 
086         * @see #chain(Comparator[])
087         * @param c0 a comparator
088         * @param c1 a comparator
089         * @param c2 a comparator
090         * @param c3 a comparator
091         * @param c4 a comparator
092         * @return a chained comparator
093         */
094        public static Comparator chain(
095          final Comparator c0, final Comparator c1, final Comparator c2, 
096          final Comparator c3, final Comparator c4 )
097        {
098            return chain( new Comparator[]{c0, c1, c2, c3, c4} );
099        }
100    
101        /**
102         * Chains comparators together.
103         * 
104         * @see #chain(Comparator[])
105         * @param comparators a List of comparators to chain together
106         * @return a chained comparator
107         */
108        public static Comparator chain( final List comparators )
109        {
110            return new Chain(
111              (Comparator[]) comparators.toArray(
112                new Comparator[ comparators.size() ] ) );
113        }
114    
115        /**
116         * Chains an array of comparators together. Each Comparator will be called
117         * in turn until one of them return a non-zero value, this value will be
118         * returned.
119         * 
120         * @param comparators the array of comparators
121         * @return a chained comparator
122         */
123        public static Comparator chain( final Comparator[] comparators ) 
124        {
125            return new Chain( comparators );
126        }
127    
128        /**
129         * Chains a series of Comparators together.
130         */
131        private static class Chain implements Comparator
132        {
133            private final Comparator[] m_chain;
134    
135            /**
136             * Creates a Comparator chain using the specified array of Comparators
137             * @param chain the Comparators in the chain
138             */
139            public Chain( final Comparator[] chain )
140            {
141                m_chain = new Comparator[ chain.length ];
142                System.arraycopy( chain, 0, m_chain, 0, chain.length );
143            }
144            
145           /**
146            * Compare two values.
147            * @param left the first value
148            * @param right the second value
149            * @return the result
150            */
151            public int compare( final Object left, final Object right )
152            {
153                int result = 0;
154                for( int i = 0; result == 0 && i < m_chain.length; ++i )
155                {
156                    result = m_chain[i].compare( left, right );
157                }
158                return result;
159            }
160        }
161    
162        /**
163         * Reverses a comparator's logic.
164         * 
165         * @param wrapped
166         *            the Comparator to reverse the logic of
167         * @return a comparator with reverse logic
168         */
169        private static Comparator reverse( final Comparator wrapped )
170        {
171            return new Reverse( wrapped );
172        }
173    
174       /**
175        * A reverse comparator.
176        */
177        private static class Reverse implements Comparator 
178        {
179            private final Comparator m_wrapped;
180    
181            /**
182             * Creates a Comparator with reverse logic
183             * @param wrapped the original logic
184             */
185            public Reverse( final Comparator wrapped )
186            {
187                m_wrapped = wrapped;
188            }
189    
190           /**
191            * Compare two values.
192            * @param left the first value
193            * @param right the second value
194            * @return the result
195            */
196            public int compare( final Object left, final Object right )
197            {
198                return -m_wrapped.compare( left, right );
199            }
200        }
201    
202        /**
203         * Forces Group instances to appear at the beginning of lists
204         * 
205         * @see Group
206         * @return a new comparator
207         */
208        public static Comparator groupFirst()
209        {
210            return new GroupFirst();
211        }
212    
213        /**
214         * Forces Group instances to appear at the end of lists
215         * 
216         * @see Group
217         * @return a new comparator
218         */
219        public static Comparator groupLast()
220        {
221            return reverse( groupFirst() );
222        }
223    
224       /**
225        * A group first comparator.
226        */
227        private static class GroupFirst implements Comparator 
228        {
229           /**
230            * Compare two values.
231            * @param left the first value
232            * @param right the second value
233            * @return the result
234            */
235            public int compare( final Object left, final Object right )
236            {
237                final boolean l = left instanceof Group;
238                final boolean r = right instanceof Group;
239    
240                if( l ^ r )
241                {
242                    if( l )
243                    {
244                        return -1;
245                    }
246                    return 1;
247                }
248                return 0;
249            }
250        }
251    
252        /**
253         * Forces Switch instances to appear at the beginning of lists
254         * 
255         * @see Switch
256         * @return a new comparator
257         */
258        public static Comparator switchFirst() 
259        {
260            return new SwitchFirst();
261        }
262    
263        /**
264         * Forces Switch instances to appear at the end of lists
265         * 
266         * @see Switch
267         * @return a new comparator
268         */
269        public static Comparator switchLast()
270        {
271            return reverse( switchFirst() );
272        }
273    
274       /**
275        * A switch first comparator.
276        */
277        private static class SwitchFirst implements Comparator 
278        {
279           /**
280            * Compare two values.
281            * @param left the first value
282            * @param right the second value
283            * @return the result
284            */
285            public int compare( final Object left, final Object right )
286            {
287                final boolean l = left instanceof Switch;
288                final boolean r = right instanceof Switch;
289    
290                if( l ^ r )
291                {
292                    if( l ) 
293                    {
294                        return -1;
295                    }
296                    return 1;
297                }
298                return 0;
299            }
300        }
301    
302        /**
303         * Forces Command instances to appear at the beginning of lists
304         * 
305         * @see Command
306         * @return a new comparator
307         */
308        public static Comparator commandFirst()
309        {
310            return new CommandFirst();
311        }
312    
313        /**
314         * Forces Command instances to appear at the end of lists
315         * 
316         * @see Command
317         * @return a new comparator
318         */
319        public static Comparator commandLast()
320        {
321            return reverse( commandFirst() );
322        }
323    
324       /**
325        * A command first comparator.
326        */
327        private static class CommandFirst implements Comparator
328        {
329           /**
330            * Compare two values.
331            * @param left the first value
332            * @param right the second value
333            * @return the result
334            */
335            public int compare( final Object left, final Object right )
336            {
337                final boolean l = left instanceof Command;
338                final boolean r = right instanceof Command;
339    
340                if( l ^ r )
341                {
342                    if( l )
343                    {
344                        return -1;
345                    }
346                    return 1;
347                }
348                return 0;
349            }
350        }
351    
352        /**
353         * Forces DefaultOption instances to appear at the beginning of lists
354         * 
355         * @see DefaultOption
356         * @return a new comparator
357         */
358        public static Comparator defaultOptionFirst()
359        {
360            return new DefaultOptionFirst();
361        }
362    
363        /**
364         * Forces DefaultOption instances to appear at the end of lists
365         * 
366         * @see DefaultOption
367         * @return a new comparator
368         */
369        public static Comparator defaultOptionLast()
370        {
371            return reverse( defaultOptionFirst() );
372        }
373    
374       /**
375        * An option first comparator.
376        */
377        private static class DefaultOptionFirst implements Comparator 
378        {
379           /**
380            * Compare two values.
381            * @param left the first value
382            * @param right the second value
383            * @return the result
384            */
385            public int compare( final Object left, final Object right )
386            {
387                final boolean l = left instanceof DefaultOption;
388                final boolean r = right instanceof DefaultOption;
389    
390                if( l ^ r )
391                {
392                    if( l )
393                    {
394                        return -1;
395                    }
396                    return 1;
397                }
398                return 0;
399            }
400        }
401    
402        /**
403         * Forces Comparators with a particular trigger to appear at the beginning
404         * of lists
405         * 
406         * @param name
407         *            the trigger name to select
408         * @see Option#getTriggers()
409         * @return a new comparator
410         */
411        public static Comparator namedFirst( final String name )
412        {
413            return new Named( name );
414        }
415    
416        /**
417         * Forces Comparators with a particular trigger to appear at the end of
418         * lists
419         * 
420         * @param name
421         *            the trigger name to select
422         * @see Option#getTriggers()
423         * @return a new comparator
424         */
425        public static Comparator namedLast( final String name )
426        {
427            return reverse( new Named( name ) );
428        }
429    
430       /**
431        * A named comparator.
432        */
433        private static class Named implements Comparator 
434        {
435            private final String m_name;
436            
437            /**
438             * Creates a Comparator that sorts a particular name high in order
439             * @param name the trigger name to select
440             */
441            public Named( final String name )
442            {
443                m_name = name;
444            }
445            
446           /**
447            * Compare two values.
448            * @param primary the first value
449            * @param secondary the second value
450            * @return the result
451            */
452            public int compare( final Object primary, final Object secondary )
453            {
454                final Option left = (Option) primary;
455                final Option right = (Option) secondary;
456    
457                final boolean l = left.getTriggers().contains( m_name );
458                final boolean r = right.getTriggers().contains( m_name );
459    
460                if( l ^ r )
461                {
462                    if( l )  
463                    {
464                        return -1;
465                    }
466                    return 1;
467                }
468                return 0;
469            }
470        }
471    
472        /**
473         * Orders Options by preferredName
474         * 
475         * @see Option#getPreferredName()
476         * @return a new comparator
477         */
478        public static Comparator preferredNameFirst()
479        {
480            return new PreferredName();
481        }
482    
483        /**
484         * Orders Options by preferredName, reversed
485         * 
486         * @see Option#getPreferredName()
487         * @return a new comparator
488         */
489        public static Comparator preferredNameLast()
490        {
491            return reverse( preferredNameFirst() );
492        }
493    
494       /**
495        * A preferred name comparator.
496        */
497        private static class PreferredName implements Comparator
498        {
499           /**
500            * Compare two values.
501            * @param primary the first value
502            * @param secondary the second value
503            * @return the result
504            */
505            public int compare( final Object primary, final Object secondary )
506            {
507                final Option left = (Option) primary;
508                final Option right = (Option) secondary;
509                return left.getPreferredName().compareTo( right.getPreferredName() );
510            }
511        }
512    
513        /**
514         * Orders Options grouping required Options first
515         * 
516         * @see Option#isRequired()
517         * @return a new comparator
518         */
519        public static Comparator requiredFirst()
520        {
521            return new Required();
522        }
523        
524        /**
525         * Orders Options grouping required Options last
526         * 
527         * @see Option#isRequired()
528         * @return a new comparator
529         */
530        public static Comparator requiredLast()
531        {
532            return reverse( requiredFirst() );
533        }
534        
535       /**
536        * A required comparator.
537        */
538        private static class Required implements Comparator
539        {
540           /**
541            * Compare two values.
542            * @param primary the first value
543            * @param secondary the second value
544            * @return the result
545            */
546            public int compare( final Object primary, final Object secondary )
547            {
548                final Option left = (Option) primary;
549                final Option right = (Option) secondary;
550                
551                final boolean l = left.isRequired();
552                final boolean r = right.isRequired();
553    
554                if( l ^ r )
555                {
556                    if( l )
557                    {
558                        return -1;
559                    }
560                    return 1;
561                }
562                return 0;
563            }
564        }
565    }