001/**
002 * Copyright 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.commandline;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.HashSet;
023import java.util.Iterator;
024import java.util.List;
025import java.util.Set;
026import java.util.StringTokenizer;
027import java.util.prefs.BackingStoreException;
028import java.util.prefs.Preferences;
029
030import net.dpml.cli.Option;
031
032/**
033 * A CommandLine implementation using the Preferences API, useful when
034 * constructing a complex DefaultingCommandLine
035 *
036 * This implementation uses the children of a single preference node to populate
037 * the CommandLine.  Options are keyed from their preferred name and presence in
038 * the Preferences object is taken as presence in the CommandLine.  Argument
039 * values are taken from the Preference value and are optionally separated using
040 * the separator char defined, at construction time.  Switch values can be
041 * specified using a simple value of <code>true</code> or <code>false</code>;
042 * obviously this means that Switches with Arguments are not supported by this
043 * implementation.
044 *
045 * @author <a href="@PUBLISHER-URL@">@PUBLISHER-NAME@</a>
046 * @version @PROJECT-VERSION@
047 * @see java.util.prefs.Preferences
048 * @see net.dpml.cli.commandline.DefaultingCommandLine
049 * @see net.dpml.cli.Option#getPreferredName() 
050 */
051public class PreferencesCommandLine extends CommandLineImpl
052{
053    private static final char NUL = '\0';
054    private final Preferences m_preferences;
055    private final Option m_root;
056    private final char m_separator;
057    
058    /**
059     * Creates a new PreferencesCommandLine using the specified root Option and
060     * Preferences node.  Argument values will be separated using the char 0.
061     * 
062     * @param root the CommandLine's root Option
063     * @param preferences the Preferences node to get values from
064     */
065    public PreferencesCommandLine( final Option root, final Preferences preferences )
066    {
067        this( root, preferences, NUL );
068    }
069    
070    /**
071     * Creates a new PreferencesCommandLine using the specified root Option,
072     * Preferences node and value separator.
073     * 
074     * @param root the CommandLine's root Option
075     * @param preferences the Preferences node to get values from
076     * @param separator the character to split argument values
077     */
078    public PreferencesCommandLine(
079      final Option root, final Preferences preferences, final char separator )
080    {
081        m_root = root;
082        m_preferences = preferences;
083        m_separator = separator;
084    }
085    
086    /**
087     * Detects the presence of an option in this CommandLine.
088     * 
089     * @param option the Option to search for
090     * @return true iff the option is present
091     */
092    public boolean hasOption( Option option )
093    {
094        if( option==null )
095        {
096            return false;
097        }
098        else
099        {
100            try 
101            {
102                return Arrays.asList( m_preferences.keys() ).contains( option.getPreferredName() );
103            } 
104            catch( BackingStoreException e )
105            {
106                return false;
107            }
108        }
109    }
110
111    /**
112     * Finds the Option with the specified trigger
113     * 
114     * @param trigger the name of the option to retrieve
115     * @return the Option matching the trigger or null if none exists
116     */
117    public Option getOption( String trigger )
118    {
119        return m_root.findOption( trigger );
120    }
121
122    /**
123     * Retrieves the Argument values associated with the specified Option
124     * 
125     * @param option the Option associated with the values
126     * @param defaultValues the result to return if no values are found
127     * @return a list of values or defaultValues if none are found
128     */
129    public List getValues( final Option option, final List defaultValues )
130    {
131        final String value = m_preferences.get( option.getPreferredName(), null );
132        if( value==null )
133        {
134            return defaultValues;
135        }
136        else if( m_separator>NUL )
137        {
138            final List values = new ArrayList();
139            final StringTokenizer tokens = new StringTokenizer( value, String.valueOf( m_separator ) );
140            
141            while( tokens.hasMoreTokens() )
142            {
143                values.add( tokens.nextToken() );
144            }
145            
146            return values;
147        }
148        else
149        {
150            return Collections.singletonList( value );
151        }
152    }
153
154    /**
155     * Retrieves the Boolean value associated with the specified Switch
156     * 
157     * @param option the Option associated with the value
158     * @param defaultValue the Boolean to use if none match
159     * @return the Boolean associated with option or defaultValue if none exists
160     */
161    public Boolean getSwitch( final Option option, final Boolean defaultValue )
162    {
163        final String value = m_preferences.get( option.getPreferredName(), null );
164        if( "true".equals( value ) )
165        {
166            return Boolean.TRUE;
167        }
168        else if( "false".equals( value ) )
169        {
170            return Boolean.FALSE;
171        }
172        else
173        {
174            return defaultValue;
175        }
176    }
177    
178    /**
179     * Retrieves the value associated with the specified property 
180     * 
181     * @param property the property name to lookup
182     * @param defaultValue the value to use if no other is found
183     * @return the value of the property or defaultValue
184     */
185    public String getProperty( final String property, final String defaultValue )
186    {
187        return m_preferences.get( property, defaultValue );
188    }
189
190    /**
191     * Retrieves the set of all property names associated with this CommandLine
192     * 
193     * @return a none null set of property names 
194     */
195    public Set getProperties()
196    {
197        try 
198        {
199            return new HashSet( Arrays.asList( m_preferences.keys() ) );
200        }
201        catch( BackingStoreException e )
202        {
203            return Collections.EMPTY_SET;
204        }
205    }
206
207    /**
208     * Retrieves a list of all Options found in this CommandLine
209     * 
210     * @return a none null list of Options
211     */
212    public List getOptions()
213    {
214        try 
215        {
216            final List options = new ArrayList();
217            final Iterator keys = Arrays.asList( m_preferences.keys() ).iterator();
218            while( keys.hasNext() ) 
219            {
220                final String trigger = (String) keys.next();
221                final Option option = m_root.findOption( trigger );
222                if( option != null )
223                {
224                    options.add( option );
225                }
226            }
227            return Collections.unmodifiableList( options );
228        } 
229        catch( BackingStoreException e )
230        {
231            return Collections.EMPTY_LIST;
232        }
233    }
234
235    /**
236     * Retrieves a list of all Option triggers found in this CommandLine
237     * 
238     * @return a none null list of Option triggers
239     */
240    public Set getOptionTriggers()
241    {
242        final Set triggers = new HashSet();
243        final Iterator options = getOptions().iterator();
244        while( options.hasNext() )
245        {
246            final Option option = (Option) options.next();
247            triggers.addAll( option.getTriggers() );
248        }
249        return Collections.unmodifiableSet( triggers );
250    }
251}