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 */
017package net.dpml.cli.util;
018
019import java.util.Comparator;
020import java.util.List;
021
022import net.dpml.cli.Group;
023import net.dpml.cli.Option;
024import net.dpml.cli.option.Command;
025import net.dpml.cli.option.DefaultOption;
026import net.dpml.cli.option.Switch;
027
028/**
029 * A collection of Comparators suitable for use with Option instances.
030 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
031 * @version @PROJECT-VERSION@
032 */
033public 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}