blob: 888db6ec929010fd2f5c59b27f8a3fdbe8c7a252 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.oodt.cas.cli.construct;
//OODT static imports
import static org.apache.oodt.cas.cli.util.CmdLineUtils.getOptionByName;
import static org.apache.oodt.cas.cli.util.CmdLineUtils.isHelpOption;
import static org.apache.oodt.cas.cli.util.CmdLineUtils.isSubOption;
import static org.apache.oodt.cas.cli.util.CmdLineUtils.sortOptionsByRequiredStatus;
//JDK imports
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Stack;
//Apache imports
import org.apache.commons.lang.Validate;
//OODT imports
import org.apache.oodt.cas.cli.exception.CmdLineConstructionException;
import org.apache.oodt.cas.cli.option.CmdLineOption;
import org.apache.oodt.cas.cli.option.CmdLineOptionInstance;
import org.apache.oodt.cas.cli.option.GroupCmdLineOption;
import org.apache.oodt.cas.cli.option.GroupSubOption;
import org.apache.oodt.cas.cli.util.CmdLineIterable;
import org.apache.oodt.cas.cli.util.ParsedArg;
//Google imports
import com.google.common.annotations.VisibleForTesting;
/**
* Standard {@link CmdLineConstructor} which support options and option
* groups.
*
* @author bfoster (Brian Foster)
*/
public class StdCmdLineConstructor implements CmdLineConstructor {
public Set<CmdLineOptionInstance> construct(CmdLineIterable<ParsedArg> parsedArgs,
Set<CmdLineOption> validOptions) throws CmdLineConstructionException {
HashSet<CmdLineOptionInstance> optionInstances = new HashSet<CmdLineOptionInstance>();
Stack<CmdLineOptionInstance> groupOptions = new Stack<CmdLineOptionInstance>();
for (ParsedArg arg : parsedArgs) {
if (arg.getType().equals(ParsedArg.Type.OPTION)) {
// check if option is a valid one
CmdLineOption option = getOptionByName(arg.getName(),
validOptions);
if (option == null) {
throw new CmdLineConstructionException("Invalid option: '" + arg.getName() + "'");
}
// read found option
CmdLineOptionInstance specifiedOption = getOption(parsedArgs, option);
// Check if we are currently loading subOptions.
if (!groupOptions.isEmpty()) {
CmdLineOptionInstance currentGroup = groupOptions.peek();
// Check if option is NOT a subOption for current group.
if (!isSubOption(currentGroup.getOption(), option)) {
// Check if current group was expecting more subOptions.
Set<CmdLineOption> requiredSubOptions = verifyGroupHasRequiredSubOptions(currentGroup);
if (!requiredSubOptions.isEmpty()) {
throw new CmdLineConstructionException(
"Missing the following required subOptions for '"
+ currentGroup.getOption()
+ "': "
+ sortOptionsByRequiredStatus(requiredSubOptions));
} else if (currentGroup.getSubOptions().isEmpty()) {
throw new CmdLineConstructionException(
"Must specify a subOption for group option '"
+ currentGroup.getOption() + "'");
} else {
// pop group and add to list of specified options.
optionInstances.add(groupOptions.pop());
}
// It is a sub-option...
} else {
// Add option to current group subOptions.
currentGroup.addSubOption(specifiedOption);
continue;
}
}
if (option instanceof GroupCmdLineOption) {
// Push group as current group.
groupOptions.push(specifiedOption);
if (!parsedArgs.hasNext()) {
throw new CmdLineConstructionException(
"Must specify a subOption for group option '"
+ specifiedOption.getOption() + "'");
}
} else if (option.isSubOption()) {
throw new CmdLineConstructionException("Option '" + option
+ "' is a subOption, but was used at top level Option");
} else {
// Option good to go.
optionInstances.add(specifiedOption);
}
} else {
throw new CmdLineConstructionException("Invalid argument: '" + arg + "'");
}
}
while (!groupOptions.isEmpty()) {
CmdLineOptionInstance currentGroup = groupOptions.pop();
Set<CmdLineOption> requiredSubOptions = verifyGroupHasRequiredSubOptions(currentGroup);
if (!requiredSubOptions.isEmpty()) {
throw new CmdLineConstructionException(
"Missing the following required subOptions for '"
+ currentGroup.getOption() + "': "
+ sortOptionsByRequiredStatus(requiredSubOptions));
} else {
optionInstances.add(currentGroup);
}
}
return optionInstances;
}
@VisibleForTesting
/* package */static CmdLineOptionInstance getOption(CmdLineIterable<ParsedArg> args,
CmdLineOption option) throws CmdLineConstructionException {
CmdLineOptionInstance specifiedOption = new CmdLineOptionInstance();
specifiedOption.setOption(option);
List<String> values = getValues(args);
if (isHelpOption(option)) {
specifiedOption.setValues(values);
} else if (option.hasArgs()) {
if (!values.isEmpty()) {
specifiedOption.setValues(values);
} else if (!option.hasStaticArgs()) {
throw new CmdLineConstructionException("Option " + option + " requires args");
}
} else if (!option.hasArgs() && !values.isEmpty()) {
throw new CmdLineConstructionException("Option " + option + " does not support args");
}
return specifiedOption;
}
@VisibleForTesting
/* package */static Set<CmdLineOption> verifyGroupHasRequiredSubOptions(
CmdLineOptionInstance group) {
Validate.isTrue(group.isGroup());
Set<CmdLineOption> missingSubOptions = new HashSet<CmdLineOption>();
TOP: for (GroupSubOption subOption : ((GroupCmdLineOption) group.getOption())
.getSubOptions()) {
if (subOption.isRequired()) {
for (CmdLineOptionInstance specifiedSubOption : group
.getSubOptions()) {
if (specifiedSubOption.getOption().equals(subOption.getOption())) {
continue TOP;
}
}
missingSubOptions.add(subOption.getOption());
}
}
return missingSubOptions;
}
@VisibleForTesting
/* package */static List<String> getValues(CmdLineIterable<ParsedArg> args) {
List<String> values = new ArrayList<String>();
ParsedArg nextValue = args.incrementAndGet();
while (nextValue != null && nextValue.getType().equals(ParsedArg.Type.VALUE)) {
values.add(nextValue.getName());
nextValue = args.incrementAndGet();
}
args.descrementIndex();
return values;
}
}