CommandLineImpl.java

/*******************************************************************************
 * Copyright (c) 2004, 2013 Steve Flasby
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * <ul>
 *     <li>Redistributions of source code must retain the above copyright notice,
 *         this list of conditions and the following disclaimer.</li>
 *     <li>Redistributions in binary form must reproduce the above copyright notice,
 *         this list of conditions and the following disclaimer in the documentation
 *         and/or other materials provided with the distribution.</li>
 * </ul>
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/
/*
 * Created on 16-Jul-2004
 *
 */
package org.flasby.cli.impl;

import java.util.ArrayList;
import java.util.HashMap;

import org.flasby.cli.CommandLine;
import org.flasby.cli.Flag;
import org.flasby.cli.MissingRequiredOptionException;
import org.flasby.cli.Option;
import org.flasby.cli.OptionList;
import org.flasby.cli.Options;
import org.flasby.cli.UnknownArgumentException;

/**
 * 
 * @author flasbyst
 * 
 */
public class CommandLineImpl implements CommandLine {

	private final HashMap<String,ArrayList<String>> mProvidedOptions = new HashMap<String,ArrayList<String>>();
	private final HashMap<String,Option> mRequiredOptions = new HashMap<String,Option>();
	private final HashMap<String,Option> mAllOptions = new HashMap<String,Option>();
	private       String mOption = "";
	private final Options mOptions;
	private final ArrayList<String> mDefaultOption = new ArrayList<String>();
	private       ArrayList<String> mCurrentOption = mDefaultOption;

	public CommandLineImpl(final Options options, String[] args) throws UnknownArgumentException, MissingRequiredOptionException {
		mOptions=new Options(options);
		for (int i = 0; i < options.getOptions().length; i++) {
			Option option = options.getOptions()[i];
			mAllOptions.put(option.getOpt(), option);
			if ( option.isMandatory() ) {
				mRequiredOptions.put( option.getOpt(), option );
			}
		}
		for (String arg2 : args) {
			String arg = arg2;
			if ("-".equals(arg)) {
				// Process default argument
				mCurrentOption = mDefaultOption;
				mOption = "";
			} else if (arg.startsWith("-")) {
				if ( arg.length()>2 ) {
					burst( arg.substring(1) );
				} else {
					// process the option
					if ( ! process(arg.substring(1)) ){
						// Its a flag & does NOT require a value
						mCurrentOption = mDefaultOption;
						mOption = "";
					}
				}
			} else {
				// Its a parameter value, add it to the current param, if any
				mCurrentOption.add( arg );
				process( mOption );
				// Value is processed, reset the processor
				mCurrentOption = mDefaultOption;
				mOption = "";
			}
		}
		checkRequiredOptions();
	}

	private void checkRequiredOptions() throws MissingRequiredOptionException {
		if ( mRequiredOptions.size()>0 ) {
			throw new MissingRequiredOptionException(
					mRequiredOptions.values().iterator().next(),
					mOptions
					);
		}
	}

	private void burst( String args ) throws UnknownArgumentException {
		for (int i = 0; i < args.length(); i++) {
			process( args.substring( i, i+1 ) );
		}
	}

	/**
	 * 
	 * @param arg
	 * @return true is an argument is required. That is, its not a Flag
	 * @throws UnknownArgumentException
	 */
	private boolean process( String arg ) throws UnknownArgumentException {
		mOption = arg;
		Option opt = mAllOptions.get( arg );
		if ( opt==null ) {
			throw new UnknownArgumentException( "-"+arg,mOptions );
		}
		mRequiredOptions.remove( opt.getOpt() );
		ArrayList<String> values = mProvidedOptions.get( opt.getOpt() );
		if ( values == null ) {
			values = new ArrayList<String>();
		}
		mProvidedOptions.put( opt.getOpt(), values );
		mCurrentOption = values;
		return ! (opt instanceof Flag);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.flasby.cli.CommandLine#wasProvided(org.flasby.cli.Option)
	 */
	@Override
	public boolean wasProvided(Option option) {
		return mProvidedOptions.containsKey(option.getOpt());
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.flasby.cli.CommandLine#getOptionValues(org.flasby.cli.Option)
	 */
	@Override
	public String[] getValues(OptionList option) {
		if (!wasProvided(option)) {
			return new String[0];
		}
		ArrayList<String> l = mProvidedOptions.get(option.getOpt());
		String[] retval = new String[l.size()];
		l.toArray(retval);
		return retval;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.flasby.cli.CommandLine#getOptionValue(org.flasby.cli.Option)
	 */
	@Override
	public String getValue(Option option) {
		if (!wasProvided(option)) {
			return option.getDefaultValue();
		}
		ArrayList<String> l = mProvidedOptions.get(option.getOpt());
		if ( l.size() == 0 ) {
			// No value, maybe it should be a default?
			String[] defaults = getDefaultValues();
			if ( defaults.length > 0 ) {
				return defaults[0];
			}
			return null;
		}
		return l.get(0);
	}

	@Override
	public boolean getValue(Flag flag) {
		return wasProvided(flag);
	}

	/* (non-Javadoc)
	 * @see org.flasby.cli.CommandLine#wasDefaultProvided()
	 */
	@Override
	public boolean wasDefaultProvided() {
		return mDefaultOption.size()>0;
	}

	/* (non-Javadoc)
	 * @see org.flasby.cli.CommandLine#getDefaultValue()
	 */
	@Override
	public String getDefaultValue() {
		String[] retval = getDefaultValues();
		if (retval.length == 0) {
			return null;
		}
		return retval[0];
	}

	/* (non-Javadoc)
	 * @see org.flasby.cli.CommandLine#getDefaultValues()
	 */
	@Override
	public String[] getDefaultValues() {
		if (!wasDefaultProvided()) {
			return new String[0];
		}
		String[] retval = new String[mDefaultOption.size()];
		mDefaultOption.toArray(retval);
		return retval;
	}

	@Override
	public Options getOptions(){
		return new Options(mOptions);
	}
}