2.9.7 changes, now with CLI

This commit is contained in:
Konloch 2015-07-16 19:28:10 -06:00
parent 44eb4de39b
commit d4c0eba2bc
45 changed files with 6978 additions and 568 deletions

View File

@ -18,6 +18,7 @@ Code from various projects has been used, including but not limited to:
Dex2Jar by pxb1..?
Krakatau by Storyyeller
JD GUI/JD Core by The Java-Decompiler Team
Enjarify by Storyyeller
Contributors:
Konloch
@ -58,6 +59,14 @@ Key Features:
Recent Files & Recent Plugins.
And more! Give it a try for yourself!
Command Line Input:
-help Displays the help menu
-list Displays the available decompilers
-decompiler <decompiler> Selects the decompiler, procyon by default
-i <input file> Selects the input file
-o <output file> Selects the output file
-nowait Doesn't wait for the user to read the CLI messages
Are you a Java Reverse Engineer? Do you want to learn?
Join The Bytecode Club Today!
https://the.bytecode.club

View File

@ -0,0 +1,87 @@
/**
* 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.commons.cli;
/**
* Thrown when more than one option in an option group
* has been provided.
*
* @version $Id: AlreadySelectedException.java 1443102 2013-02-06 18:12:16Z tn $
*/
public class AlreadySelectedException extends ParseException
{
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = 3674381532418544760L;
/** The option group selected. */
private OptionGroup group;
/** The option that triggered the exception. */
private Option option;
/**
* Construct a new <code>AlreadySelectedException</code>
* with the specified detail message.
*
* @param message the detail message
*/
public AlreadySelectedException(String message)
{
super(message);
}
/**
* Construct a new <code>AlreadySelectedException</code>
* for the specified option group.
*
* @param group the option group already selected
* @param option the option that triggered the exception
* @since 1.2
*/
public AlreadySelectedException(OptionGroup group, Option option)
{
this("The option '" + option.getKey() + "' was specified but an option from this group "
+ "has already been selected: '" + group.getSelected() + "'");
this.group = group;
this.option = option;
}
/**
* Returns the option group where another option has been selected.
*
* @return the related option group
* @since 1.2
*/
public OptionGroup getOptionGroup()
{
return group;
}
/**
* Returns the option that was added to the group and triggered the exception.
*
* @return the related option
* @since 1.2
*/
public Option getOption()
{
return option;
}
}

View File

@ -0,0 +1,88 @@
/**
* 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.commons.cli;
import java.util.Collection;
import java.util.Iterator;
/**
* Exception thrown when an option can't be identified from a partial name.
*
* @version $Id: AmbiguousOptionException.java 1669814 2015-03-28 18:09:26Z britter $
* @since 1.3
*/
public class AmbiguousOptionException extends UnrecognizedOptionException
{
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = 5829816121277947229L;
/** The list of options matching the partial name specified */
private final Collection<String> matchingOptions;
/**
* Constructs a new AmbiguousOptionException.
*
* @param option the partial option name
* @param matchingOptions the options matching the name
*/
public AmbiguousOptionException(String option, Collection<String> matchingOptions)
{
super(createMessage(option, matchingOptions), option);
this.matchingOptions = matchingOptions;
}
/**
* Returns the options matching the partial name.
* @return a collection of options matching the name
*/
public Collection<String> getMatchingOptions()
{
return matchingOptions;
}
/**
* Build the exception message from the specified list of options.
*
* @param option
* @param matchingOptions
* @return
*/
private static String createMessage(String option, Collection<String> matchingOptions)
{
StringBuilder buf = new StringBuilder("Ambiguous option: '");
buf.append(option);
buf.append("' (could be: ");
Iterator<String> it = matchingOptions.iterator();
while (it.hasNext())
{
buf.append("'");
buf.append(it.next());
buf.append("'");
if (it.hasNext())
{
buf.append(", ");
}
}
buf.append(")");
return buf.toString();
}
}

View File

@ -0,0 +1,51 @@
/**
* 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.commons.cli;
/**
* The class BasicParser provides a very simple implementation of
* the {@link Parser#flatten(Options,String[],boolean) flatten} method.
*
* @version $Id: BasicParser.java 1443102 2013-02-06 18:12:16Z tn $
* @deprecated since 1.3, use the {@link DefaultParser} instead
*/
@Deprecated
public class BasicParser extends Parser
{
/**
* <p>A simple implementation of {@link Parser}'s abstract
* {@link Parser#flatten(Options, String[], boolean) flatten} method.</p>
*
* <p><b>Note:</b> <code>options</code> and <code>stopAtNonOption</code>
* are not used in this <code>flatten</code> method.</p>
*
* @param options The command line {@link Options}
* @param arguments The command line arguments to be parsed
* @param stopAtNonOption Specifies whether to stop flattening
* when an non option is found.
* @return The <code>arguments</code> String array.
*/
@Override
protected String[] flatten(@SuppressWarnings("unused") Options options,
String[] arguments,
@SuppressWarnings("unused") boolean stopAtNonOption)
{
// just echo the arguments
return arguments;
}
}

View File

@ -0,0 +1,380 @@
/**
* 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.commons.cli;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
/**
* Represents list of arguments parsed against a {@link Options} descriptor.
* <p>
* It allows querying of a boolean {@link #hasOption(String opt)},
* in addition to retrieving the {@link #getOptionValue(String opt)}
* for options requiring arguments.
* <p>
* Additionally, any left-over or unrecognized arguments,
* are available for further processing.
*
* @version $Id: CommandLine.java 1444365 2013-02-09 14:21:27Z tn $
*/
public class CommandLine implements Serializable
{
/** The serial version UID. */
private static final long serialVersionUID = 1L;
/** the unrecognised options/arguments */
private final List<String> args = new LinkedList<String>();
/** the processed options */
private final List<Option> options = new ArrayList<Option>();
/**
* Creates a command line.
*/
protected CommandLine()
{
// nothing to do
}
/**
* Query to see if an option has been set.
*
* @param opt Short name of the option
* @return true if set, false if not
*/
public boolean hasOption(String opt)
{
return options.contains(resolveOption(opt));
}
/**
* Query to see if an option has been set.
*
* @param opt character name of the option
* @return true if set, false if not
*/
public boolean hasOption(char opt)
{
return hasOption(String.valueOf(opt));
}
/**
* Return the <code>Object</code> type of this <code>Option</code>.
*
* @param opt the name of the option
* @return the type of this <code>Option</code>
* @deprecated due to System.err message. Instead use getParsedOptionValue(String)
*/
@Deprecated
public Object getOptionObject(String opt)
{
try
{
return getParsedOptionValue(opt);
}
catch (ParseException pe)
{
System.err.println("Exception found converting " + opt + " to desired type: " + pe.getMessage());
return null;
}
}
/**
* Return a version of this <code>Option</code> converted to a particular type.
*
* @param opt the name of the option
* @return the value parsed into a particular object
* @throws ParseException if there are problems turning the option value into the desired type
* @see PatternOptionBuilder
* @since 1.2
*/
public Object getParsedOptionValue(String opt) throws ParseException
{
String res = getOptionValue(opt);
Option option = resolveOption(opt);
if (option == null || res == null)
{
return null;
}
return TypeHandler.createValue(res, option.getType());
}
/**
* Return the <code>Object</code> type of this <code>Option</code>.
*
* @param opt the name of the option
* @return the type of opt
*/
public Object getOptionObject(char opt)
{
return getOptionObject(String.valueOf(opt));
}
/**
* Retrieve the first argument, if any, of this option.
*
* @param opt the name of the option
* @return Value of the argument if option is set, and has an argument,
* otherwise null.
*/
public String getOptionValue(String opt)
{
String[] values = getOptionValues(opt);
return (values == null) ? null : values[0];
}
/**
* Retrieve the first argument, if any, of this option.
*
* @param opt the character name of the option
* @return Value of the argument if option is set, and has an argument,
* otherwise null.
*/
public String getOptionValue(char opt)
{
return getOptionValue(String.valueOf(opt));
}
/**
* Retrieves the array of values, if any, of an option.
*
* @param opt string name of the option
* @return Values of the argument if option is set, and has an argument,
* otherwise null.
*/
public String[] getOptionValues(String opt)
{
List<String> values = new ArrayList<String>();
for (Option option : options)
{
if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt()))
{
values.addAll(option.getValuesList());
}
}
return values.isEmpty() ? null : values.toArray(new String[values.size()]);
}
/**
* Retrieves the option object given the long or short option as a String
*
* @param opt short or long name of the option
* @return Canonicalized option
*/
private Option resolveOption(String opt)
{
opt = Util.stripLeadingHyphens(opt);
for (Option option : options)
{
if (opt.equals(option.getOpt()))
{
return option;
}
if (opt.equals(option.getLongOpt()))
{
return option;
}
}
return null;
}
/**
* Retrieves the array of values, if any, of an option.
*
* @param opt character name of the option
* @return Values of the argument if option is set, and has an argument,
* otherwise null.
*/
public String[] getOptionValues(char opt)
{
return getOptionValues(String.valueOf(opt));
}
/**
* Retrieve the first argument, if any, of an option.
*
* @param opt name of the option
* @param defaultValue is the default value to be returned if the option
* is not specified
* @return Value of the argument if option is set, and has an argument,
* otherwise <code>defaultValue</code>.
*/
public String getOptionValue(String opt, String defaultValue)
{
String answer = getOptionValue(opt);
return (answer != null) ? answer : defaultValue;
}
/**
* Retrieve the argument, if any, of an option.
*
* @param opt character name of the option
* @param defaultValue is the default value to be returned if the option
* is not specified
* @return Value of the argument if option is set, and has an argument,
* otherwise <code>defaultValue</code>.
*/
public String getOptionValue(char opt, String defaultValue)
{
return getOptionValue(String.valueOf(opt), defaultValue);
}
/**
* Retrieve the map of values associated to the option. This is convenient
* for options specifying Java properties like <tt>-Dparam1=value1
* -Dparam2=value2</tt>. The first argument of the option is the key, and
* the 2nd argument is the value. If the option has only one argument
* (<tt>-Dfoo</tt>) it is considered as a boolean flag and the value is
* <tt>"true"</tt>.
*
* @param opt name of the option
* @return The Properties mapped by the option, never <tt>null</tt>
* even if the option doesn't exists
* @since 1.2
*/
public Properties getOptionProperties(String opt)
{
Properties props = new Properties();
for (Option option : options)
{
if (opt.equals(option.getOpt()) || opt.equals(option.getLongOpt()))
{
List<String> values = option.getValuesList();
if (values.size() >= 2)
{
// use the first 2 arguments as the key/value pair
props.put(values.get(0), values.get(1));
}
else if (values.size() == 1)
{
// no explicit value, handle it as a boolean
props.put(values.get(0), "true");
}
}
}
return props;
}
/**
* Retrieve any left-over non-recognized options and arguments
*
* @return remaining items passed in but not parsed as an array
*/
public String[] getArgs()
{
String[] answer = new String[args.size()];
args.toArray(answer);
return answer;
}
/**
* Retrieve any left-over non-recognized options and arguments
*
* @return remaining items passed in but not parsed as a <code>List</code>.
*/
public List<String> getArgList()
{
return args;
}
/**
* jkeyes
* - commented out until it is implemented properly
* <p>Dump state, suitable for debugging.</p>
*
* @return Stringified form of this object
*/
/*
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append("[ CommandLine: [ options: ");
buf.append(options.toString());
buf.append(" ] [ args: ");
buf.append(args.toString());
buf.append(" ] ]");
return buf.toString();
}
*/
/**
* Add left-over unrecognized option/argument.
*
* @param arg the unrecognised option/argument.
*/
protected void addArg(String arg)
{
args.add(arg);
}
/**
* Add an option to the command line. The values of the option are stored.
*
* @param opt the processed option
*/
protected void addOption(Option opt)
{
options.add(opt);
}
/**
* Returns an iterator over the Option members of CommandLine.
*
* @return an <code>Iterator</code> over the processed {@link Option}
* members of this {@link CommandLine}
*/
public Iterator<Option> iterator()
{
return options.iterator();
}
/**
* Returns an array of the processed {@link Option}s.
*
* @return an array of the processed {@link Option}s.
*/
public Option[] getOptions()
{
Collection<Option> processed = options;
// reinitialise array
Option[] optionsArray = new Option[processed.size()];
// return the array
return processed.toArray(optionsArray);
}
}

View File

@ -0,0 +1,98 @@
/**
* 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.commons.cli;
/**
* A class that implements the <code>CommandLineParser</code> interface
* can parse a String array according to the {@link Options} specified
* and return a {@link CommandLine}.
*
* @version $Id: CommandLineParser.java 1443102 2013-02-06 18:12:16Z tn $
*/
public interface CommandLineParser
{
/**
* Parse the arguments according to the specified options.
*
* @param options the specified Options
* @param arguments the command line arguments
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
CommandLine parse(Options options, String[] arguments) throws ParseException;
/**
* Parse the arguments according to the specified options and
* properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
/* To maintain binary compatibility, this is commented out.
It is still in the abstract Parser class, so most users will
still reap the benefit.
CommandLine parse(Options options, String[] arguments, Properties properties)
throws ParseException;
*/
/**
* Parse the arguments according to the specified options.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
* the parsing and the remaining arguments are added to the
* {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
* argument triggers a ParseException.
*
* @return the list of atomic option and value tokens
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException;
/**
* Parse the arguments according to the specified options and
* properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
* the parsing and the remaining arguments are added to the
* {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
* argument triggers a ParseException.
*
* @return the list of atomic option and value tokens
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
/* To maintain binary compatibility, this is commented out.
It is still in the abstract Parser class, so most users will
still reap the benefit.
CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
throws ParseException;
*/
}

View File

@ -0,0 +1,694 @@
/**
* 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.commons.cli;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;
/**
* Default parser.
*
* @version $Id: DefaultParser.java 1677454 2015-05-03 17:13:54Z ggregory $
* @since 1.3
*/
public class DefaultParser implements CommandLineParser
{
/** The command-line instance. */
protected CommandLine cmd;
/** The current options. */
protected Options options;
/**
* Flag indicating how unrecognized tokens are handled. <tt>true</tt> to stop
* the parsing and add the remaining tokens to the args list.
* <tt>false</tt> to throw an exception.
*/
protected boolean stopAtNonOption;
/** The token currently processed. */
protected String currentToken;
/** The last option parsed. */
protected Option currentOption;
/** Flag indicating if tokens should no longer be analyzed and simply added as arguments of the command line. */
protected boolean skipParsing;
/** The required options and groups expected to be found when parsing the command line. */
protected List expectedOpts;
public CommandLine parse(Options options, String[] arguments) throws ParseException
{
return parse(options, arguments, null);
}
/**
* Parse the arguments according to the specified options and properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException
{
return parse(options, arguments, properties, false);
}
public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
{
return parse(options, arguments, null, stopAtNonOption);
}
/**
* Parse the arguments according to the specified options and properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
* the parsing and the remaining arguments are added to the
* {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
* argument triggers a ParseException.
*
* @return the list of atomic option and value tokens
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
throws ParseException
{
this.options = options;
this.stopAtNonOption = stopAtNonOption;
skipParsing = false;
currentOption = null;
expectedOpts = new ArrayList(options.getRequiredOptions());
// clear the data from the groups
for (OptionGroup group : options.getOptionGroups())
{
group.setSelected(null);
}
cmd = new CommandLine();
if (arguments != null)
{
for (String argument : arguments)
{
handleToken(argument);
}
}
// check the arguments of the last option
checkRequiredArgs();
// add the default options
handleProperties(properties);
checkRequiredOptions();
return cmd;
}
/**
* Sets the values of Options using the values in <code>properties</code>.
*
* @param properties The value properties to be processed.
*/
private void handleProperties(Properties properties) throws ParseException
{
if (properties == null)
{
return;
}
for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();)
{
String option = e.nextElement().toString();
Option opt = options.getOption(option);
if (opt == null)
{
throw new UnrecognizedOptionException("Default option wasn't defined", option);
}
// if the option is part of a group, check if another option of the group has been selected
OptionGroup group = options.getOptionGroup(opt);
boolean selected = group != null && group.getSelected() != null;
if (!cmd.hasOption(option) && !selected)
{
// get the value from the properties
String value = properties.getProperty(option);
if (opt.hasArg())
{
if (opt.getValues() == null || opt.getValues().length == 0)
{
opt.addValueForProcessing(value);
}
}
else if (!("yes".equalsIgnoreCase(value)
|| "true".equalsIgnoreCase(value)
|| "1".equalsIgnoreCase(value)))
{
// if the value is not yes, true or 1 then don't add the option to the CommandLine
continue;
}
handleOption(opt);
currentOption = null;
}
}
}
/**
* Throws a {@link MissingOptionException} if all of the required options
* are not present.
*
* @throws MissingOptionException if any of the required Options
* are not present.
*/
private void checkRequiredOptions() throws MissingOptionException
{
// if there are required options that have not been processed
if (!expectedOpts.isEmpty())
{
throw new MissingOptionException(expectedOpts);
}
}
/**
* Throw a {@link MissingArgumentException} if the current option
* didn't receive the number of arguments expected.
*/
private void checkRequiredArgs() throws ParseException
{
if (currentOption != null && currentOption.requiresArg())
{
throw new MissingArgumentException(currentOption);
}
}
/**
* Handle any command line token.
*
* @param token the command line token to handle
* @throws ParseException
*/
private void handleToken(String token) throws ParseException
{
currentToken = token;
if (skipParsing)
{
cmd.addArg(token);
}
else if ("--".equals(token))
{
skipParsing = true;
}
else if (currentOption != null && currentOption.acceptsArg() && isArgument(token))
{
currentOption.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(token));
}
else if (token.startsWith("--"))
{
handleLongOption(token);
}
else if (token.startsWith("-") && !"-".equals(token))
{
handleShortAndLongOption(token);
}
else
{
handleUnknownToken(token);
}
if (currentOption != null && !currentOption.acceptsArg())
{
currentOption = null;
}
}
/**
* Returns true is the token is a valid argument.
*
* @param token
*/
private boolean isArgument(String token)
{
return !isOption(token) || isNegativeNumber(token);
}
/**
* Check if the token is a negative number.
*
* @param token
*/
private boolean isNegativeNumber(String token)
{
try
{
Double.parseDouble(token);
return true;
}
catch (NumberFormatException e)
{
return false;
}
}
/**
* Tells if the token looks like an option.
*
* @param token
*/
private boolean isOption(String token)
{
return isLongOption(token) || isShortOption(token);
}
/**
* Tells if the token looks like a short option.
*
* @param token
*/
private boolean isShortOption(String token)
{
// short options (-S, -SV, -S=V, -SV1=V2, -S1S2)
return token.startsWith("-") && token.length() >= 2 && options.hasShortOption(token.substring(1, 2));
}
/**
* Tells if the token looks like a long option.
*
* @param token
*/
private boolean isLongOption(String token)
{
if (!token.startsWith("-") || token.length() == 1)
{
return false;
}
int pos = token.indexOf("=");
String t = pos == -1 ? token : token.substring(0, pos);
if (!options.getMatchingOptions(t).isEmpty())
{
// long or partial long options (--L, -L, --L=V, -L=V, --l, --l=V)
return true;
}
else if (getLongPrefix(token) != null && !token.startsWith("--"))
{
// -LV
return true;
}
return false;
}
/**
* Handles an unknown token. If the token starts with a dash an
* UnrecognizedOptionException is thrown. Otherwise the token is added
* to the arguments of the command line. If the stopAtNonOption flag
* is set, this stops the parsing and the remaining tokens are added
* as-is in the arguments of the command line.
*
* @param token the command line token to handle
*/
private void handleUnknownToken(String token) throws ParseException
{
if (token.startsWith("-") && token.length() > 1 && !stopAtNonOption)
{
throw new UnrecognizedOptionException("Unrecognized option: " + token, token);
}
cmd.addArg(token);
if (stopAtNonOption)
{
skipParsing = true;
}
}
/**
* Handles the following tokens:
*
* --L
* --L=V
* --L V
* --l
*
* @param token the command line token to handle
*/
private void handleLongOption(String token) throws ParseException
{
if (token.indexOf('=') == -1)
{
handleLongOptionWithoutEqual(token);
}
else
{
handleLongOptionWithEqual(token);
}
}
/**
* Handles the following tokens:
*
* --L
* -L
* --l
* -l
*
* @param token the command line token to handle
*/
private void handleLongOptionWithoutEqual(String token) throws ParseException
{
List<String> matchingOpts = options.getMatchingOptions(token);
if (matchingOpts.isEmpty())
{
handleUnknownToken(currentToken);
}
else if (matchingOpts.size() > 1)
{
throw new AmbiguousOptionException(token, matchingOpts);
}
else
{
handleOption(options.getOption(matchingOpts.get(0)));
}
}
/**
* Handles the following tokens:
*
* --L=V
* -L=V
* --l=V
* -l=V
*
* @param token the command line token to handle
*/
private void handleLongOptionWithEqual(String token) throws ParseException
{
int pos = token.indexOf('=');
String value = token.substring(pos + 1);
String opt = token.substring(0, pos);
List<String> matchingOpts = options.getMatchingOptions(opt);
if (matchingOpts.isEmpty())
{
handleUnknownToken(currentToken);
}
else if (matchingOpts.size() > 1)
{
throw new AmbiguousOptionException(opt, matchingOpts);
}
else
{
Option option = options.getOption(matchingOpts.get(0));
if (option.acceptsArg())
{
handleOption(option);
currentOption.addValueForProcessing(value);
currentOption = null;
}
else
{
handleUnknownToken(currentToken);
}
}
}
/**
* Handles the following tokens:
*
* -S
* -SV
* -S V
* -S=V
* -S1S2
* -S1S2 V
* -SV1=V2
*
* -L
* -LV
* -L V
* -L=V
* -l
*
* @param token the command line token to handle
*/
private void handleShortAndLongOption(String token) throws ParseException
{
String t = Util.stripLeadingHyphens(token);
int pos = t.indexOf('=');
if (t.length() == 1)
{
// -S
if (options.hasShortOption(t))
{
handleOption(options.getOption(t));
}
else
{
handleUnknownToken(token);
}
}
else if (pos == -1)
{
// no equal sign found (-xxx)
if (options.hasShortOption(t))
{
handleOption(options.getOption(t));
}
else if (!options.getMatchingOptions(t).isEmpty())
{
// -L or -l
handleLongOptionWithoutEqual(token);
}
else
{
// look for a long prefix (-Xmx512m)
String opt = getLongPrefix(t);
if (opt != null && options.getOption(opt).acceptsArg())
{
handleOption(options.getOption(opt));
currentOption.addValueForProcessing(t.substring(opt.length()));
currentOption = null;
}
else if (isJavaProperty(t))
{
// -SV1 (-Dflag)
handleOption(options.getOption(t.substring(0, 1)));
currentOption.addValueForProcessing(t.substring(1));
currentOption = null;
}
else
{
// -S1S2S3 or -S1S2V
handleConcatenatedOptions(token);
}
}
}
else
{
// equal sign found (-xxx=yyy)
String opt = t.substring(0, pos);
String value = t.substring(pos + 1);
if (opt.length() == 1)
{
// -S=V
Option option = options.getOption(opt);
if (option != null && option.acceptsArg())
{
handleOption(option);
currentOption.addValueForProcessing(value);
currentOption = null;
}
else
{
handleUnknownToken(token);
}
}
else if (isJavaProperty(opt))
{
// -SV1=V2 (-Dkey=value)
handleOption(options.getOption(opt.substring(0, 1)));
currentOption.addValueForProcessing(opt.substring(1));
currentOption.addValueForProcessing(value);
currentOption = null;
}
else
{
// -L=V or -l=V
handleLongOptionWithEqual(token);
}
}
}
/**
* Search for a prefix that is the long name of an option (-Xmx512m)
*
* @param token
*/
private String getLongPrefix(String token)
{
String t = Util.stripLeadingHyphens(token);
int i;
String opt = null;
for (i = t.length() - 2; i > 1; i--)
{
String prefix = t.substring(0, i);
if (options.hasLongOption(prefix))
{
opt = prefix;
break;
}
}
return opt;
}
/**
* Check if the specified token is a Java-like property (-Dkey=value).
*/
private boolean isJavaProperty(String token)
{
String opt = token.substring(0, 1);
Option option = options.getOption(opt);
return option != null && (option.getArgs() >= 2 || option.getArgs() == Option.UNLIMITED_VALUES);
}
private void handleOption(Option option) throws ParseException
{
// check the previous option before handling the next one
checkRequiredArgs();
option = (Option) option.clone();
updateRequiredOptions(option);
cmd.addOption(option);
if (option.hasArg())
{
currentOption = option;
}
else
{
currentOption = null;
}
}
/**
* Removes the option or its group from the list of expected elements.
*
* @param option
*/
private void updateRequiredOptions(Option option) throws AlreadySelectedException
{
if (option.isRequired())
{
expectedOpts.remove(option.getKey());
}
// if the option is in an OptionGroup make that option the selected option of the group
if (options.getOptionGroup(option) != null)
{
OptionGroup group = options.getOptionGroup(option);
if (group.isRequired())
{
expectedOpts.remove(group);
}
group.setSelected(option);
}
}
/**
* Breaks <code>token</code> into its constituent parts
* using the following algorithm.
*
* <ul>
* <li>ignore the first character ("<b>-</b>")</li>
* <li>foreach remaining character check if an {@link Option}
* exists with that id.</li>
* <li>if an {@link Option} does exist then add that character
* prepended with "<b>-</b>" to the list of processed tokens.</li>
* <li>if the {@link Option} can have an argument value and there
* are remaining characters in the token then add the remaining
* characters as a token to the list of processed tokens.</li>
* <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
* <code>stopAtNonOption</code> <b>IS</b> set then add the special token
* "<b>--</b>" followed by the remaining characters and also
* the remaining tokens directly to the processed tokens list.</li>
* <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
* <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
* character prepended with "<b>-</b>".</li>
* </ul>
*
* @param token The current token to be <b>burst</b>
* at the first non-Option encountered.
* @throws ParseException if there are any problems encountered
* while parsing the command line token.
*/
protected void handleConcatenatedOptions(String token) throws ParseException
{
for (int i = 1; i < token.length(); i++)
{
String ch = String.valueOf(token.charAt(i));
if (options.hasOption(ch))
{
handleOption(options.getOption(ch));
if (currentOption != null && token.length() != i + 1)
{
// add the trail as an argument of the option
currentOption.addValueForProcessing(token.substring(i + 1));
break;
}
}
else
{
handleUnknownToken(stopAtNonOption && i > 1 ? token.substring(i) : token);
break;
}
}
}
}

View File

@ -0,0 +1,115 @@
/**
* 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.commons.cli;
import java.util.ArrayList;
import java.util.List;
/**
* The class GnuParser provides an implementation of the
* {@link Parser#flatten(Options, String[], boolean) flatten} method.
*
* @version $Id: GnuParser.java 1445352 2013-02-12 20:48:19Z tn $
* @deprecated since 1.3, use the {@link DefaultParser} instead
*/
@Deprecated
public class GnuParser extends Parser
{
/**
* This flatten method does so using the following rules:
* <ol>
* <li>If an {@link Option} exists for the first character of
* the <code>arguments</code> entry <b>AND</b> an {@link Option}
* does not exist for the whole <code>argument</code> then
* add the first character as an option to the processed tokens
* list e.g. "-D" and add the rest of the entry to the also.</li>
* <li>Otherwise just add the token to the processed tokens list.</li>
* </ol>
*
* @param options The Options to parse the arguments by.
* @param arguments The arguments that have to be flattened.
* @param stopAtNonOption specifies whether to stop flattening when
* a non option has been encountered
* @return a String array of the flattened arguments
*/
@Override
protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption)
{
List<String> tokens = new ArrayList<String>();
boolean eatTheRest = false;
for (int i = 0; i < arguments.length; i++)
{
String arg = arguments[i];
if ("--".equals(arg))
{
eatTheRest = true;
tokens.add("--");
}
else if ("-".equals(arg))
{
tokens.add("-");
}
else if (arg.startsWith("-"))
{
String opt = Util.stripLeadingHyphens(arg);
if (options.hasOption(opt))
{
tokens.add(arg);
}
else
{
if (opt.indexOf('=') != -1 && options.hasOption(opt.substring(0, opt.indexOf('='))))
{
// the format is --foo=value or -foo=value
tokens.add(arg.substring(0, arg.indexOf('='))); // --foo
tokens.add(arg.substring(arg.indexOf('=') + 1)); // value
}
else if (options.hasOption(arg.substring(0, 2)))
{
// the format is a special properties option (-Dproperty=value)
tokens.add(arg.substring(0, 2)); // -D
tokens.add(arg.substring(2)); // property=value
}
else
{
eatTheRest = stopAtNonOption;
tokens.add(arg);
}
}
}
else
{
tokens.add(arg);
}
if (eatTheRest)
{
for (i++; i < arguments.length; i++) //NOPMD
{
tokens.add(arguments[i]);
}
}
}
return tokens.toArray(new String[tokens.size()]);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,71 @@
/**
* 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.commons.cli;
/**
* Thrown when an option requiring an argument
* is not provided with an argument.
*
* @version $Id: MissingArgumentException.java 1443102 2013-02-06 18:12:16Z tn $
*/
public class MissingArgumentException extends ParseException
{
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = -7098538588704965017L;
/** The option requiring additional arguments */
private Option option;
/**
* Construct a new <code>MissingArgumentException</code>
* with the specified detail message.
*
* @param message the detail message
*/
public MissingArgumentException(String message)
{
super(message);
}
/**
* Construct a new <code>MissingArgumentException</code>
* with the specified detail message.
*
* @param option the option requiring an argument
* @since 1.2
*/
public MissingArgumentException(Option option)
{
this("Missing argument for option: " + option.getKey());
this.option = option;
}
/**
* Return the option requiring an argument that wasn't provided
* on the command line.
*
* @return the related option
* @since 1.2
*/
public Option getOption()
{
return option;
}
}

View File

@ -0,0 +1,96 @@
/**
* 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.commons.cli;
import java.util.List;
import java.util.Iterator;
/**
* Thrown when a required option has not been provided.
*
* @version $Id: MissingOptionException.java 1443102 2013-02-06 18:12:16Z tn $
*/
public class MissingOptionException extends ParseException
{
/** This exception {@code serialVersionUID}. */
private static final long serialVersionUID = 8161889051578563249L;
/** The list of missing options and groups */
private List missingOptions;
/**
* Construct a new <code>MissingSelectedException</code>
* with the specified detail message.
*
* @param message the detail message
*/
public MissingOptionException(String message)
{
super(message);
}
/**
* Constructs a new <code>MissingSelectedException</code> with the
* specified list of missing options.
*
* @param missingOptions the list of missing options and groups
* @since 1.2
*/
public MissingOptionException(List missingOptions)
{
this(createMessage(missingOptions));
this.missingOptions = missingOptions;
}
/**
* Returns the list of options or option groups missing in the command line parsed.
*
* @return the missing options, consisting of String instances for simple
* options, and OptionGroup instances for required option groups.
* @since 1.2
*/
public List getMissingOptions()
{
return missingOptions;
}
/**
* Build the exception message from the specified list of options.
*
* @param missingOptions the list of missing options and groups
* @since 1.2
*/
private static String createMessage(List<?> missingOptions)
{
StringBuilder buf = new StringBuilder("Missing required option");
buf.append(missingOptions.size() == 1 ? "" : "s");
buf.append(": ");
Iterator<?> it = missingOptions.iterator();
while (it.hasNext())
{
buf.append(it.next());
if (it.hasNext())
{
buf.append(", ");
}
}
return buf.toString();
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,396 @@
/**
* 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.commons.cli;
/**
* OptionBuilder allows the user to create Options using descriptive methods.
* <p>
* Details on the Builder pattern can be found at
* <a href="http://c2.com/cgi-bin/wiki?BuilderPattern">http://c2.com/cgi-bin/wiki?BuilderPattern</a>.
* <p>
* This class is NOT thread safe. See <a href="https://issues.apache.org/jira/browse/CLI-209">CLI-209</a>
*
* @version $Id: OptionBuilder.java 1677400 2015-05-03 13:46:08Z britter $
* @since 1.0
* @deprecated since 1.3, use {@link Option#builder(String)} instead
*/
@Deprecated
public final class OptionBuilder
{
/** long option */
private static String longopt;
/** option description */
private static String description;
/** argument name */
private static String argName;
/** is required? */
private static boolean required;
/** the number of arguments */
private static int numberOfArgs = Option.UNINITIALIZED;
/** option type */
private static Class<?> type;
/** option can have an optional argument value */
private static boolean optionalArg;
/** value separator for argument value */
private static char valuesep;
/** option builder instance */
private static final OptionBuilder INSTANCE = new OptionBuilder();
static
{
// ensure the consistency of the initial values
reset();
}
/**
* private constructor to prevent instances being created
*/
private OptionBuilder()
{
// hide the constructor
}
/**
* Resets the member variables to their default values.
*/
private static void reset()
{
description = null;
argName = null;
longopt = null;
type = String.class;
required = false;
numberOfArgs = Option.UNINITIALIZED;
optionalArg = false;
valuesep = (char) 0;
}
/**
* The next Option created will have the following long option value.
*
* @param newLongopt the long option value
* @return the OptionBuilder instance
*/
public static OptionBuilder withLongOpt(String newLongopt)
{
OptionBuilder.longopt = newLongopt;
return INSTANCE;
}
/**
* The next Option created will require an argument value.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder hasArg()
{
OptionBuilder.numberOfArgs = 1;
return INSTANCE;
}
/**
* The next Option created will require an argument value if
* <code>hasArg</code> is true.
*
* @param hasArg if true then the Option has an argument value
* @return the OptionBuilder instance
*/
public static OptionBuilder hasArg(boolean hasArg)
{
OptionBuilder.numberOfArgs = hasArg ? 1 : Option.UNINITIALIZED;
return INSTANCE;
}
/**
* The next Option created will have the specified argument value name.
*
* @param name the name for the argument value
* @return the OptionBuilder instance
*/
public static OptionBuilder withArgName(String name)
{
OptionBuilder.argName = name;
return INSTANCE;
}
/**
* The next Option created will be required.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder isRequired()
{
OptionBuilder.required = true;
return INSTANCE;
}
/**
* The next Option created uses <code>sep</code> as a means to
* separate argument values.
* <p>
* <b>Example:</b>
* <pre>
* Option opt = OptionBuilder.withValueSeparator('=')
* .create('D');
*
* String args = "-Dkey=value";
* CommandLine line = parser.parse(args);
* String propertyName = opt.getValue(0); // will be "key"
* String propertyValue = opt.getValue(1); // will be "value"
* </pre>
*
* @param sep The value separator to be used for the argument values.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder withValueSeparator(char sep)
{
OptionBuilder.valuesep = sep;
return INSTANCE;
}
/**
* The next Option created uses '<code>=</code>' as a means to
* separate argument values.
*
* <b>Example:</b>
* <pre>
* Option opt = OptionBuilder.withValueSeparator()
* .create('D');
*
* CommandLine line = parser.parse(args);
* String propertyName = opt.getValue(0);
* String propertyValue = opt.getValue(1);
* </pre>
*
* @return the OptionBuilder instance
*/
public static OptionBuilder withValueSeparator()
{
OptionBuilder.valuesep = '=';
return INSTANCE;
}
/**
* The next Option created will be required if <code>required</code>
* is true.
*
* @param newRequired if true then the Option is required
* @return the OptionBuilder instance
*/
public static OptionBuilder isRequired(boolean newRequired)
{
OptionBuilder.required = newRequired;
return INSTANCE;
}
/**
* The next Option created can have unlimited argument values.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder hasArgs()
{
OptionBuilder.numberOfArgs = Option.UNLIMITED_VALUES;
return INSTANCE;
}
/**
* The next Option created can have <code>num</code> argument values.
*
* @param num the number of args that the option can have
* @return the OptionBuilder instance
*/
public static OptionBuilder hasArgs(int num)
{
OptionBuilder.numberOfArgs = num;
return INSTANCE;
}
/**
* The next Option can have an optional argument.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder hasOptionalArg()
{
OptionBuilder.numberOfArgs = 1;
OptionBuilder.optionalArg = true;
return INSTANCE;
}
/**
* The next Option can have an unlimited number of optional arguments.
*
* @return the OptionBuilder instance
*/
public static OptionBuilder hasOptionalArgs()
{
OptionBuilder.numberOfArgs = Option.UNLIMITED_VALUES;
OptionBuilder.optionalArg = true;
return INSTANCE;
}
/**
* The next Option can have the specified number of optional arguments.
*
* @param numArgs - the maximum number of optional arguments
* the next Option created can have.
* @return the OptionBuilder instance
*/
public static OptionBuilder hasOptionalArgs(int numArgs)
{
OptionBuilder.numberOfArgs = numArgs;
OptionBuilder.optionalArg = true;
return INSTANCE;
}
/**
* The next Option created will have a value that will be an instance
* of <code>type</code>.
* <p>
* <b>Note:</b> this method is kept for binary compatibility and the
* input type is supposed to be a {@link Class} object.
*
* @param newType the type of the Options argument value
* @return the OptionBuilder instance
* @deprecated since 1.3, use {@link #withType(Class)} instead
*/
@Deprecated
public static OptionBuilder withType(Object newType)
{
return withType((Class<?>) newType);
}
/**
* The next Option created will have a value that will be an instance
* of <code>type</code>.
*
* @param newType the type of the Options argument value
* @return the OptionBuilder instance
* @since 1.3
*/
public static OptionBuilder withType(Class<?> newType)
{
OptionBuilder.type = newType;
return INSTANCE;
}
/**
* The next Option created will have the specified description
*
* @param newDescription a description of the Option's purpose
* @return the OptionBuilder instance
*/
public static OptionBuilder withDescription(String newDescription)
{
OptionBuilder.description = newDescription;
return INSTANCE;
}
/**
* Create an Option using the current settings and with
* the specified Option <code>char</code>.
*
* @param opt the character representation of the Option
* @return the Option instance
* @throws IllegalArgumentException if <code>opt</code> is not
* a valid character. See Option.
*/
public static Option create(char opt) throws IllegalArgumentException
{
return create(String.valueOf(opt));
}
/**
* Create an Option using the current settings
*
* @return the Option instance
* @throws IllegalArgumentException if <code>longOpt</code> has not been set.
*/
public static Option create() throws IllegalArgumentException
{
if (longopt == null)
{
OptionBuilder.reset();
throw new IllegalArgumentException("must specify longopt");
}
return create(null);
}
/**
* Create an Option using the current settings and with
* the specified Option <code>char</code>.
*
* @param opt the <code>java.lang.String</code> representation
* of the Option
* @return the Option instance
* @throws IllegalArgumentException if <code>opt</code> is not
* a valid character. See Option.
*/
public static Option create(String opt) throws IllegalArgumentException
{
Option option = null;
try
{
// create the option
option = new Option(opt, description);
// set the option properties
option.setLongOpt(longopt);
option.setRequired(required);
option.setOptionalArg(optionalArg);
option.setArgs(numberOfArgs);
option.setType(type);
option.setValueSeparator(valuesep);
option.setArgName(argName);
}
finally
{
// reset the OptionBuilder properties
OptionBuilder.reset();
}
// return the Option instance
return option;
}
}

View File

@ -0,0 +1,179 @@
/**
* 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.commons.cli;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
/**
* A group of mutually exclusive options.
*
* @version $Id: OptionGroup.java 1669814 2015-03-28 18:09:26Z britter $
*/
public class OptionGroup implements Serializable
{
/** The serial version UID. */
private static final long serialVersionUID = 1L;
/** hold the options */
private final Map<String, Option> optionMap = new HashMap<String, Option>();
/** the name of the selected option */
private String selected;
/** specified whether this group is required */
private boolean required;
/**
* Add the specified <code>Option</code> to this group.
*
* @param option the option to add to this group
* @return this option group with the option added
*/
public OptionGroup addOption(Option option)
{
// key - option name
// value - the option
optionMap.put(option.getKey(), option);
return this;
}
/**
* @return the names of the options in this group as a
* <code>Collection</code>
*/
public Collection<String> getNames()
{
// the key set is the collection of names
return optionMap.keySet();
}
/**
* @return the options in this group as a <code>Collection</code>
*/
public Collection<Option> getOptions()
{
// the values are the collection of options
return optionMap.values();
}
/**
* Set the selected option of this group to <code>name</code>.
*
* @param option the option that is selected
* @throws AlreadySelectedException if an option from this group has
* already been selected.
*/
public void setSelected(Option option) throws AlreadySelectedException
{
if (option == null)
{
// reset the option previously selected
selected = null;
return;
}
// if no option has already been selected or the
// same option is being reselected then set the
// selected member variable
if (selected == null || selected.equals(option.getKey()))
{
selected = option.getKey();
}
else
{
throw new AlreadySelectedException(this, option);
}
}
/**
* @return the selected option name
*/
public String getSelected()
{
return selected;
}
/**
* @param required specifies if this group is required
*/
public void setRequired(boolean required)
{
this.required = required;
}
/**
* Returns whether this option group is required.
*
* @return whether this option group is required
*/
public boolean isRequired()
{
return required;
}
/**
* Returns the stringified version of this OptionGroup.
*
* @return the stringified representation of this group
*/
@Override
public String toString()
{
StringBuilder buff = new StringBuilder();
Iterator<Option> iter = getOptions().iterator();
buff.append("[");
while (iter.hasNext())
{
Option option = iter.next();
if (option.getOpt() != null)
{
buff.append("-");
buff.append(option.getOpt());
}
else
{
buff.append("--");
buff.append(option.getLongOpt());
}
if (option.getDescription() != null)
{
buff.append(" ");
buff.append(option.getDescription());
}
if (iter.hasNext())
{
buff.append(", ");
}
}
buff.append("]");
return buff.toString();
}
}

View File

@ -0,0 +1,99 @@
/**
* 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.commons.cli;
/**
* Validates an Option string.
*
* @version $Id: OptionValidator.java 1544819 2013-11-23 15:34:31Z tn $
* @since 1.1
*/
final class OptionValidator
{
/**
* Validates whether <code>opt</code> is a permissible Option
* shortOpt. The rules that specify if the <code>opt</code>
* is valid are:
*
* <ul>
* <li>a single character <code>opt</code> that is either
* ' '(special case), '?', '@' or a letter</li>
* <li>a multi character <code>opt</code> that only contains
* letters.</li>
* </ul>
* <p>
* In case {@code opt} is {@code null} no further validation is performed.
*
* @param opt The option string to validate, may be null
* @throws IllegalArgumentException if the Option is not valid.
*/
static void validateOption(String opt) throws IllegalArgumentException
{
// if opt is NULL do not check further
if (opt == null)
{
return;
}
// handle the single character opt
if (opt.length() == 1)
{
char ch = opt.charAt(0);
if (!isValidOpt(ch))
{
throw new IllegalArgumentException("Illegal option name '" + ch + "'");
}
}
// handle the multi character opt
else
{
for (char ch : opt.toCharArray())
{
if (!isValidChar(ch))
{
throw new IllegalArgumentException("The option '" + opt + "' contains an illegal "
+ "character : '" + ch + "'");
}
}
}
}
/**
* Returns whether the specified character is a valid Option.
*
* @param c the option to validate
* @return true if <code>c</code> is a letter, '?' or '@', otherwise false.
*/
private static boolean isValidOpt(char c)
{
return isValidChar(c) || c == '?' || c == '@';
}
/**
* Returns whether the specified character is a valid character.
*
* @param c the character to validate
* @return true if <code>c</code> is a letter.
*/
private static boolean isValidChar(char c)
{
return Character.isJavaIdentifierPart(c);
}
}

View File

@ -0,0 +1,327 @@
/**
* 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.commons.cli;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Main entry-point into the library.
* <p>
* Options represents a collection of {@link Option} objects, which
* describe the possible options for a command-line.
* <p>
* It may flexibly parse long and short options, with or without
* values. Additionally, it may parse only a portion of a commandline,
* allowing for flexible multi-stage parsing.
*
* @see org.apache.commons.cli.CommandLine
*
* @version $Id: Options.java 1685376 2015-06-14 09:51:59Z britter $
*/
public class Options implements Serializable
{
/** The serial version UID. */
private static final long serialVersionUID = 1L;
/** a map of the options with the character key */
private final Map<String, Option> shortOpts = new LinkedHashMap<String, Option>();
/** a map of the options with the long key */
private final Map<String, Option> longOpts = new LinkedHashMap<String, Option>();
/** a map of the required options */
// N.B. This can contain either a String (addOption) or an OptionGroup (addOptionGroup)
// TODO this seems wrong
private final List<Object> requiredOpts = new ArrayList<Object>();
/** a map of the option groups */
private final Map<String, OptionGroup> optionGroups = new HashMap<String, OptionGroup>();
/**
* Add the specified option group.
*
* @param group the OptionGroup that is to be added
* @return the resulting Options instance
*/
public Options addOptionGroup(OptionGroup group)
{
if (group.isRequired())
{
requiredOpts.add(group);
}
for (Option option : group.getOptions())
{
// an Option cannot be required if it is in an
// OptionGroup, either the group is required or
// nothing is required
option.setRequired(false);
addOption(option);
optionGroups.put(option.getKey(), group);
}
return this;
}
/**
* Lists the OptionGroups that are members of this Options instance.
*
* @return a Collection of OptionGroup instances.
*/
Collection<OptionGroup> getOptionGroups()
{
return new HashSet<OptionGroup>(optionGroups.values());
}
/**
* Add an option that only contains a short name.
* The option does not take an argument.
*
* @param opt Short single-character name of the option.
* @param description Self-documenting description
* @return the resulting Options instance
* @since 1.3
*/
public Options addOption(String opt, String description)
{
addOption(opt, null, false, description);
return this;
}
/**
* Add an option that only contains a short-name.
* It may be specified as requiring an argument.
*
* @param opt Short single-character name of the option.
* @param hasArg flag signally if an argument is required after this option
* @param description Self-documenting description
* @return the resulting Options instance
*/
public Options addOption(String opt, boolean hasArg, String description)
{
addOption(opt, null, hasArg, description);
return this;
}
/**
* Add an option that contains a short-name and a long-name.
* It may be specified as requiring an argument.
*
* @param opt Short single-character name of the option.
* @param longOpt Long multi-character name of the option.
* @param hasArg flag signally if an argument is required after this option
* @param description Self-documenting description
* @return the resulting Options instance
*/
public Options addOption(String opt, String longOpt, boolean hasArg, String description)
{
addOption(new Option(opt, longOpt, hasArg, description));
return this;
}
/**
* Adds an option instance
*
* @param opt the option that is to be added
* @return the resulting Options instance
*/
public Options addOption(Option opt)
{
String key = opt.getKey();
// add it to the long option list
if (opt.hasLongOpt())
{
longOpts.put(opt.getLongOpt(), opt);
}
// if the option is required add it to the required list
if (opt.isRequired())
{
if (requiredOpts.contains(key))
{
requiredOpts.remove(requiredOpts.indexOf(key));
}
requiredOpts.add(key);
}
shortOpts.put(key, opt);
return this;
}
/**
* Retrieve a read-only list of options in this set
*
* @return read-only Collection of {@link Option} objects in this descriptor
*/
public Collection<Option> getOptions()
{
return Collections.unmodifiableCollection(helpOptions());
}
/**
* Returns the Options for use by the HelpFormatter.
*
* @return the List of Options
*/
List<Option> helpOptions()
{
return new ArrayList<Option>(shortOpts.values());
}
/**
* Returns the required options.
*
* @return read-only List of required options
*/
public List getRequiredOptions()
{
return Collections.unmodifiableList(requiredOpts);
}
/**
* Retrieve the {@link Option} matching the long or short name specified.
* The leading hyphens in the name are ignored (up to 2).
*
* @param opt short or long name of the {@link Option}
* @return the option represented by opt
*/
public Option getOption(String opt)
{
opt = Util.stripLeadingHyphens(opt);
if (shortOpts.containsKey(opt))
{
return shortOpts.get(opt);
}
return longOpts.get(opt);
}
/**
* Returns the options with a long name starting with the name specified.
*
* @param opt the partial name of the option
* @return the options matching the partial name specified, or an empty list if none matches
* @since 1.3
*/
public List<String> getMatchingOptions(String opt)
{
opt = Util.stripLeadingHyphens(opt);
List<String> matchingOpts = new ArrayList<String>();
// for a perfect match return the single option only
if (longOpts.keySet().contains(opt))
{
return Collections.singletonList(opt);
}
for (String longOpt : longOpts.keySet())
{
if (longOpt.startsWith(opt))
{
matchingOpts.add(longOpt);
}
}
return matchingOpts;
}
/**
* Returns whether the named {@link Option} is a member of this {@link Options}.
*
* @param opt short or long name of the {@link Option}
* @return true if the named {@link Option} is a member of this {@link Options}
*/
public boolean hasOption(String opt)
{
opt = Util.stripLeadingHyphens(opt);
return shortOpts.containsKey(opt) || longOpts.containsKey(opt);
}
/**
* Returns whether the named {@link Option} is a member of this {@link Options}.
*
* @param opt long name of the {@link Option}
* @return true if the named {@link Option} is a member of this {@link Options}
* @since 1.3
*/
public boolean hasLongOption(String opt)
{
opt = Util.stripLeadingHyphens(opt);
return longOpts.containsKey(opt);
}
/**
* Returns whether the named {@link Option} is a member of this {@link Options}.
*
* @param opt short name of the {@link Option}
* @return true if the named {@link Option} is a member of this {@link Options}
* @since 1.3
*/
public boolean hasShortOption(String opt)
{
opt = Util.stripLeadingHyphens(opt);
return shortOpts.containsKey(opt);
}
/**
* Returns the OptionGroup the <code>opt</code> belongs to.
* @param opt the option whose OptionGroup is being queried.
*
* @return the OptionGroup if <code>opt</code> is part
* of an OptionGroup, otherwise return null
*/
public OptionGroup getOptionGroup(Option opt)
{
return optionGroups.get(opt.getKey());
}
/**
* Dump state, suitable for debugging.
*
* @return Stringified form of this object
*/
@Override
public String toString()
{
StringBuilder buf = new StringBuilder();
buf.append("[ Options: [ short ");
buf.append(shortOpts.toString());
buf.append(" ] [ long ");
buf.append(longOpts);
buf.append(" ]");
return buf.toString();
}
}

View File

@ -0,0 +1,42 @@
/**
* 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.commons.cli;
/**
* Base for Exceptions thrown during parsing of a command-line.
*
* @version $Id: ParseException.java 1443102 2013-02-06 18:12:16Z tn $
*/
public class ParseException extends Exception
{
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = 9112808380089253192L;
/**
* Construct a new <code>ParseException</code>
* with the specified detail message.
*
* @param message the detail message
*/
public ParseException(String message)
{
super(message);
}
}

View File

@ -0,0 +1,430 @@
/**
* 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.commons.cli;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.ListIterator;
import java.util.Properties;
/**
* <code>Parser</code> creates {@link CommandLine}s.
*
* @version $Id: Parser.java 1677406 2015-05-03 14:27:31Z britter $
* @deprecated since 1.3, the two-pass parsing with the flatten method is not enough flexible to handle complex cases
*/
@Deprecated
public abstract class Parser implements CommandLineParser
{
/** commandline instance */
protected CommandLine cmd;
/** current Options */
private Options options;
/** list of required options strings */
private List requiredOptions;
protected void setOptions(Options options)
{
this.options = options;
this.requiredOptions = new ArrayList(options.getRequiredOptions());
}
protected Options getOptions()
{
return options;
}
protected List getRequiredOptions()
{
return requiredOptions;
}
/**
* Subclasses must implement this method to reduce
* the <code>arguments</code> that have been passed to the parse method.
*
* @param opts The Options to parse the arguments by.
* @param arguments The arguments that have to be flattened.
* @param stopAtNonOption specifies whether to stop
* flattening when a non option has been encountered
* @return a String array of the flattened arguments
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
protected abstract String[] flatten(Options opts, String[] arguments, boolean stopAtNonOption)
throws ParseException;
/**
* Parses the specified <code>arguments</code> based
* on the specified {@link Options}.
*
* @param options the <code>Options</code>
* @param arguments the <code>arguments</code>
* @return the <code>CommandLine</code>
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*/
public CommandLine parse(Options options, String[] arguments) throws ParseException
{
return parse(options, arguments, null, false);
}
/**
* Parse the arguments according to the specified options and properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @return the list of atomic option and value tokens
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*
* @since 1.1
*/
public CommandLine parse(Options options, String[] arguments, Properties properties) throws ParseException
{
return parse(options, arguments, properties, false);
}
/**
* Parses the specified <code>arguments</code>
* based on the specified {@link Options}.
*
* @param options the <code>Options</code>
* @param arguments the <code>arguments</code>
* @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
* the parsing and the remaining arguments are added to the
* {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
* argument triggers a ParseException.
* @return the <code>CommandLine</code>
* @throws ParseException if an error occurs when parsing the arguments.
*/
public CommandLine parse(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
{
return parse(options, arguments, null, stopAtNonOption);
}
/**
* Parse the arguments according to the specified options and
* properties.
*
* @param options the specified Options
* @param arguments the command line arguments
* @param properties command line option name-value pairs
* @param stopAtNonOption if <tt>true</tt> an unrecognized argument stops
* the parsing and the remaining arguments are added to the
* {@link CommandLine}s args list. If <tt>false</tt> an unrecognized
* argument triggers a ParseException.
*
* @return the list of atomic option and value tokens
*
* @throws ParseException if there are any problems encountered
* while parsing the command line tokens.
*
* @since 1.1
*/
public CommandLine parse(Options options, String[] arguments, Properties properties, boolean stopAtNonOption)
throws ParseException
{
// clear out the data in options in case it's been used before (CLI-71)
for (Option opt : options.helpOptions())
{
opt.clearValues();
}
// clear the data from the groups
for (OptionGroup group : options.getOptionGroups())
{
group.setSelected(null);
}
// initialise members
setOptions(options);
cmd = new CommandLine();
boolean eatTheRest = false;
if (arguments == null)
{
arguments = new String[0];
}
List<String> tokenList = Arrays.asList(flatten(getOptions(), arguments, stopAtNonOption));
ListIterator<String> iterator = tokenList.listIterator();
// process each flattened token
while (iterator.hasNext())
{
String t = iterator.next();
// the value is the double-dash
if ("--".equals(t))
{
eatTheRest = true;
}
// the value is a single dash
else if ("-".equals(t))
{
if (stopAtNonOption)
{
eatTheRest = true;
}
else
{
cmd.addArg(t);
}
}
// the value is an option
else if (t.startsWith("-"))
{
if (stopAtNonOption && !getOptions().hasOption(t))
{
eatTheRest = true;
cmd.addArg(t);
}
else
{
processOption(t, iterator);
}
}
// the value is an argument
else
{
cmd.addArg(t);
if (stopAtNonOption)
{
eatTheRest = true;
}
}
// eat the remaining tokens
if (eatTheRest)
{
while (iterator.hasNext())
{
String str = iterator.next();
// ensure only one double-dash is added
if (!"--".equals(str))
{
cmd.addArg(str);
}
}
}
}
processProperties(properties);
checkRequiredOptions();
return cmd;
}
/**
* Sets the values of Options using the values in <code>properties</code>.
*
* @param properties The value properties to be processed.
* @throws ParseException if there are any problems encountered
* while processing the properties.
*/
protected void processProperties(Properties properties) throws ParseException
{
if (properties == null)
{
return;
}
for (Enumeration<?> e = properties.propertyNames(); e.hasMoreElements();)
{
String option = e.nextElement().toString();
Option opt = options.getOption(option);
if (opt == null)
{
throw new UnrecognizedOptionException("Default option wasn't defined", option);
}
// if the option is part of a group, check if another option of the group has been selected
OptionGroup group = options.getOptionGroup(opt);
boolean selected = group != null && group.getSelected() != null;
if (!cmd.hasOption(option) && !selected)
{
// get the value from the properties instance
String value = properties.getProperty(option);
if (opt.hasArg())
{
if (opt.getValues() == null || opt.getValues().length == 0)
{
try
{
opt.addValueForProcessing(value);
}
catch (RuntimeException exp) //NOPMD
{
// if we cannot add the value don't worry about it
}
}
}
else if (!("yes".equalsIgnoreCase(value)
|| "true".equalsIgnoreCase(value)
|| "1".equalsIgnoreCase(value)))
{
// if the value is not yes, true or 1 then don't add the
// option to the CommandLine
continue;
}
cmd.addOption(opt);
updateRequiredOptions(opt);
}
}
}
/**
* Throws a {@link MissingOptionException} if all of the required options
* are not present.
*
* @throws MissingOptionException if any of the required Options are not present.
*/
protected void checkRequiredOptions() throws MissingOptionException
{
// if there are required options that have not been processed
if (!getRequiredOptions().isEmpty())
{
throw new MissingOptionException(getRequiredOptions());
}
}
/**
* Process the argument values for the specified Option
* <code>opt</code> using the values retrieved from the
* specified iterator <code>iter</code>.
*
* @param opt The current Option
* @param iter The iterator over the flattened command line Options.
*
* @throws ParseException if an argument value is required
* and it is has not been found.
*/
public void processArgs(Option opt, ListIterator<String> iter) throws ParseException
{
// loop until an option is found
while (iter.hasNext())
{
String str = iter.next();
// found an Option, not an argument
if (getOptions().hasOption(str) && str.startsWith("-"))
{
iter.previous();
break;
}
// found a value
try
{
opt.addValueForProcessing(Util.stripLeadingAndTrailingQuotes(str));
}
catch (RuntimeException exp)
{
iter.previous();
break;
}
}
if (opt.getValues() == null && !opt.hasOptionalArg())
{
throw new MissingArgumentException(opt);
}
}
/**
* Process the Option specified by <code>arg</code> using the values
* retrieved from the specified iterator <code>iter</code>.
*
* @param arg The String value representing an Option
* @param iter The iterator over the flattened command line arguments.
*
* @throws ParseException if <code>arg</code> does not represent an Option
*/
protected void processOption(String arg, ListIterator<String> iter) throws ParseException
{
boolean hasOption = getOptions().hasOption(arg);
// if there is no option throw an UnrecognisedOptionException
if (!hasOption)
{
throw new UnrecognizedOptionException("Unrecognized option: " + arg, arg);
}
// get the option represented by arg
Option opt = (Option) getOptions().getOption(arg).clone();
// update the required options and groups
updateRequiredOptions(opt);
// if the option takes an argument value
if (opt.hasArg())
{
processArgs(opt, iter);
}
// set the option on the command line
cmd.addOption(opt);
}
/**
* Removes the option or its group from the list of expected elements.
*
* @param opt
*/
private void updateRequiredOptions(Option opt) throws ParseException
{
// if the option is a required option remove the option from
// the requiredOptions list
if (opt.isRequired())
{
getRequiredOptions().remove(opt.getKey());
}
// if the option is in an OptionGroup make that option the selected
// option of the group
if (getOptions().getOptionGroup(opt) != null)
{
OptionGroup group = getOptions().getOptionGroup(opt);
if (group.isRequired())
{
getRequiredOptions().remove(group);
}
group.setSelected(opt);
}
}
}

View File

@ -0,0 +1,207 @@
/**
* 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.commons.cli;
import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.util.Date;
/**
* <p>Allows Options to be created from a single String.
* The pattern contains various single character flags and via
* an optional punctuation character, their expected type.
* </p>
*
* <table border="1">
* <caption>Overview of PatternOptionBuilder patterns</caption>
* <tr><td>a</td><td>-a flag</td></tr>
* <tr><td>b@</td><td>-b [classname]</td></tr>
* <tr><td>c&gt;</td><td>-c [filename]</td></tr>
* <tr><td>d+</td><td>-d [classname] (creates object via empty constructor)</td></tr>
* <tr><td>e%</td><td>-e [number] (creates Double/Long instance depending on existing of a '.')</td></tr>
* <tr><td>f/</td><td>-f [url]</td></tr>
* <tr><td>g:</td><td>-g [string]</td></tr>
* </table>
*
* <p>
* For example, the following allows command line flags of '-v -p string-value -f /dir/file'.
* The exclamation mark precede a mandatory option.
* </p>
*
* <pre>
* Options options = PatternOptionBuilder.parsePattern("vp:!f/");
* </pre>
*
* <p>
* TODO: These need to break out to OptionType and also to be pluggable.
* </p>
*
* @version $Id: PatternOptionBuilder.java 1677406 2015-05-03 14:27:31Z britter $
*/
public class PatternOptionBuilder
{
/** String class */
public static final Class<String> STRING_VALUE = String.class;
/** Object class */
public static final Class<Object> OBJECT_VALUE = Object.class;
/** Number class */
public static final Class<Number> NUMBER_VALUE = Number.class;
/** Date class */
public static final Class<Date> DATE_VALUE = Date.class;
/** Class class */
public static final Class<?> CLASS_VALUE = Class.class;
/// can we do this one??
// is meant to check that the file exists, else it errors.
// ie) it's for reading not writing.
/** FileInputStream class */
public static final Class<FileInputStream> EXISTING_FILE_VALUE = FileInputStream.class;
/** File class */
public static final Class<File> FILE_VALUE = File.class;
/** File array class */
public static final Class<File[]> FILES_VALUE = File[].class;
/** URL class */
public static final Class<URL> URL_VALUE = URL.class;
/**
* Retrieve the class that <code>ch</code> represents.
*
* @param ch the specified character
* @return The class that <code>ch</code> represents
*/
public static Object getValueClass(char ch)
{
switch (ch)
{
case '@':
return PatternOptionBuilder.OBJECT_VALUE;
case ':':
return PatternOptionBuilder.STRING_VALUE;
case '%':
return PatternOptionBuilder.NUMBER_VALUE;
case '+':
return PatternOptionBuilder.CLASS_VALUE;
case '#':
return PatternOptionBuilder.DATE_VALUE;
case '<':
return PatternOptionBuilder.EXISTING_FILE_VALUE;
case '>':
return PatternOptionBuilder.FILE_VALUE;
case '*':
return PatternOptionBuilder.FILES_VALUE;
case '/':
return PatternOptionBuilder.URL_VALUE;
}
return null;
}
/**
* Returns whether <code>ch</code> is a value code, i.e.
* whether it represents a class in a pattern.
*
* @param ch the specified character
* @return true if <code>ch</code> is a value code, otherwise false.
*/
public static boolean isValueCode(char ch)
{
return ch == '@'
|| ch == ':'
|| ch == '%'
|| ch == '+'
|| ch == '#'
|| ch == '<'
|| ch == '>'
|| ch == '*'
|| ch == '/'
|| ch == '!';
}
/**
* Returns the {@link Options} instance represented by <code>pattern</code>.
*
* @param pattern the pattern string
* @return The {@link Options} instance
*/
public static Options parsePattern(String pattern)
{
char opt = ' ';
boolean required = false;
Class<?> type = null;
Options options = new Options();
for (int i = 0; i < pattern.length(); i++)
{
char ch = pattern.charAt(i);
// a value code comes after an option and specifies
// details about it
if (!isValueCode(ch))
{
if (opt != ' ')
{
final Option option = Option.builder(String.valueOf(opt))
.hasArg(type != null)
.required(required)
.type(type)
.build();
// we have a previous one to deal with
options.addOption(option);
required = false;
type = null;
opt = ' ';
}
opt = ch;
}
else if (ch == '!')
{
required = true;
}
else
{
type = (Class<?>) getValueClass(ch);
}
}
if (opt != ' ')
{
final Option option = Option.builder(String.valueOf(opt))
.hasArg(type != null)
.required(required)
.type(type)
.build();
// we have a final one to deal with
options.addOption(option);
}
return options;
}
}

View File

@ -0,0 +1,294 @@
/**
* 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.commons.cli;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* The class PosixParser provides an implementation of the
* {@link Parser#flatten(Options,String[],boolean) flatten} method.
*
* @version $Id: PosixParser.java 1677451 2015-05-03 17:09:29Z ggregory $
* @deprecated since 1.3, use the {@link DefaultParser} instead
*/
@Deprecated
public class PosixParser extends Parser
{
/** holder for flattened tokens */
private final List<String> tokens = new ArrayList<String>();
/** specifies if bursting should continue */
private boolean eatTheRest;
/** holder for the current option */
private Option currentOption;
/** the command line Options */
private Options options;
/**
* Resets the members to their original state i.e. remove
* all of <code>tokens</code> entries and set <code>eatTheRest</code>
* to false.
*/
private void init()
{
eatTheRest = false;
tokens.clear();
}
/**
* <p>An implementation of {@link Parser}'s abstract
* {@link Parser#flatten(Options,String[],boolean) flatten} method.</p>
*
* <p>The following are the rules used by this flatten method.</p>
* <ol>
* <li>if <code>stopAtNonOption</code> is <b>true</b> then do not
* burst anymore of <code>arguments</code> entries, just add each
* successive entry without further processing. Otherwise, ignore
* <code>stopAtNonOption</code>.</li>
* <li>if the current <code>arguments</code> entry is "<b>--</b>"
* just add the entry to the list of processed tokens</li>
* <li>if the current <code>arguments</code> entry is "<b>-</b>"
* just add the entry to the list of processed tokens</li>
* <li>if the current <code>arguments</code> entry is two characters
* in length and the first character is "<b>-</b>" then check if this
* is a valid {@link Option} id. If it is a valid id, then add the
* entry to the list of processed tokens and set the current {@link Option}
* member. If it is not a valid id and <code>stopAtNonOption</code>
* is true, then the remaining entries are copied to the list of
* processed tokens. Otherwise, the current entry is ignored.</li>
* <li>if the current <code>arguments</code> entry is more than two
* characters in length and the first character is "<b>-</b>" then
* we need to burst the entry to determine its constituents. For more
* information on the bursting algorithm see
* {@link PosixParser#burstToken(String, boolean) burstToken}.</li>
* <li>if the current <code>arguments</code> entry is not handled
* by any of the previous rules, then the entry is added to the list
* of processed tokens.</li>
* </ol>
*
* @param options The command line {@link Options}
* @param arguments The command line arguments to be parsed
* @param stopAtNonOption Specifies whether to stop flattening
* when an non option is found.
* @return The flattened <code>arguments</code> String array.
*/
@Override
protected String[] flatten(Options options, String[] arguments, boolean stopAtNonOption) throws ParseException
{
init();
this.options = options;
// an iterator for the command line tokens
Iterator<String> iter = Arrays.asList(arguments).iterator();
// process each command line token
while (iter.hasNext())
{
// get the next command line token
String token = iter.next();
// single or double hyphen
if ("-".equals(token) || "--".equals(token))
{
tokens.add(token);
}
// handle long option --foo or --foo=bar
else if (token.startsWith("--"))
{
int pos = token.indexOf('=');
String opt = pos == -1 ? token : token.substring(0, pos); // --foo
List<String> matchingOpts = options.getMatchingOptions(opt);
if (matchingOpts.isEmpty())
{
processNonOptionToken(token, stopAtNonOption);
}
else if (matchingOpts.size() > 1)
{
throw new AmbiguousOptionException(opt, matchingOpts);
}
else
{
currentOption = options.getOption(matchingOpts.get(0));
tokens.add("--" + currentOption.getLongOpt());
if (pos != -1)
{
tokens.add(token.substring(pos + 1));
}
}
}
else if (token.startsWith("-"))
{
if (token.length() == 2 || options.hasOption(token))
{
processOptionToken(token, stopAtNonOption);
}
else if (!options.getMatchingOptions(token).isEmpty())
{
List<String> matchingOpts = options.getMatchingOptions(token);
if (matchingOpts.size() > 1)
{
throw new AmbiguousOptionException(token, matchingOpts);
}
Option opt = options.getOption(matchingOpts.get(0));
processOptionToken("-" + opt.getLongOpt(), stopAtNonOption);
}
// requires bursting
else
{
burstToken(token, stopAtNonOption);
}
}
else
{
processNonOptionToken(token, stopAtNonOption);
}
gobble(iter);
}
return tokens.toArray(new String[tokens.size()]);
}
/**
* Adds the remaining tokens to the processed tokens list.
*
* @param iter An iterator over the remaining tokens
*/
private void gobble(Iterator<String> iter)
{
if (eatTheRest)
{
while (iter.hasNext())
{
tokens.add(iter.next());
}
}
}
/**
* Add the special token "<b>--</b>" and the current <code>value</code>
* to the processed tokens list. Then add all the remaining
* <code>argument</code> values to the processed tokens list.
*
* @param value The current token
*/
private void processNonOptionToken(String value, boolean stopAtNonOption)
{
if (stopAtNonOption && (currentOption == null || !currentOption.hasArg()))
{
eatTheRest = true;
tokens.add("--");
}
tokens.add(value);
}
/**
* <p>If an {@link Option} exists for <code>token</code> then
* add the token to the processed list.</p>
*
* <p>If an {@link Option} does not exist and <code>stopAtNonOption</code>
* is set then add the remaining tokens to the processed tokens list
* directly.</p>
*
* @param token The current option token
* @param stopAtNonOption Specifies whether flattening should halt
* at the first non option.
*/
private void processOptionToken(String token, boolean stopAtNonOption)
{
if (stopAtNonOption && !options.hasOption(token))
{
eatTheRest = true;
}
if (options.hasOption(token))
{
currentOption = options.getOption(token);
}
tokens.add(token);
}
/**
* Breaks <code>token</code> into its constituent parts
* using the following algorithm.
*
* <ul>
* <li>ignore the first character ("<b>-</b>")</li>
* <li>foreach remaining character check if an {@link Option}
* exists with that id.</li>
* <li>if an {@link Option} does exist then add that character
* prepended with "<b>-</b>" to the list of processed tokens.</li>
* <li>if the {@link Option} can have an argument value and there
* are remaining characters in the token then add the remaining
* characters as a token to the list of processed tokens.</li>
* <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
* <code>stopAtNonOption</code> <b>IS</b> set then add the special token
* "<b>--</b>" followed by the remaining characters and also
* the remaining tokens directly to the processed tokens list.</li>
* <li>if an {@link Option} does <b>NOT</b> exist <b>AND</b>
* <code>stopAtNonOption</code> <b>IS NOT</b> set then add that
* character prepended with "<b>-</b>".</li>
* </ul>
*
* @param token The current token to be <b>burst</b>
* @param stopAtNonOption Specifies whether to stop processing
* at the first non-Option encountered.
*/
protected void burstToken(String token, boolean stopAtNonOption)
{
for (int i = 1; i < token.length(); i++)
{
String ch = String.valueOf(token.charAt(i));
if (options.hasOption(ch))
{
tokens.add("-" + ch);
currentOption = options.getOption(ch);
if (currentOption.hasArg() && token.length() != i + 1)
{
tokens.add(token.substring(i + 1));
break;
}
}
else if (stopAtNonOption)
{
processNonOptionToken(token.substring(i), true);
break;
}
else
{
tokens.add(token);
break;
}
}
}
}

View File

@ -0,0 +1,241 @@
/**
* 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.commons.cli;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Date;
/**
* This is a temporary implementation. TypeHandler will handle the
* pluggableness of OptionTypes and it will direct all of these types
* of conversion functionalities to ConvertUtils component in Commons
* already. BeanUtils I think.
*
* @version $Id: TypeHandler.java 1677452 2015-05-03 17:10:00Z ggregory $
*/
public class TypeHandler
{
/**
* Returns the <code>Object</code> of type <code>obj</code>
* with the value of <code>str</code>.
*
* @param str the command line value
* @param obj the type of argument
* @return The instance of <code>obj</code> initialised with
* the value of <code>str</code>.
* @throws ParseException if the value creation for the given object type failed
*/
public static Object createValue(String str, Object obj) throws ParseException
{
return createValue(str, (Class<?>) obj);
}
/**
* Returns the <code>Object</code> of type <code>clazz</code>
* with the value of <code>str</code>.
*
* @param str the command line value
* @param clazz the type of argument
* @return The instance of <code>clazz</code> initialised with
* the value of <code>str</code>.
* @throws ParseException if the value creation for the given class failed
*/
public static Object createValue(String str, Class<?> clazz) throws ParseException
{
if (PatternOptionBuilder.STRING_VALUE == clazz)
{
return str;
}
else if (PatternOptionBuilder.OBJECT_VALUE == clazz)
{
return createObject(str);
}
else if (PatternOptionBuilder.NUMBER_VALUE == clazz)
{
return createNumber(str);
}
else if (PatternOptionBuilder.DATE_VALUE == clazz)
{
return createDate(str);
}
else if (PatternOptionBuilder.CLASS_VALUE == clazz)
{
return createClass(str);
}
else if (PatternOptionBuilder.FILE_VALUE == clazz)
{
return createFile(str);
}
else if (PatternOptionBuilder.EXISTING_FILE_VALUE == clazz)
{
return createFile(str);
}
else if (PatternOptionBuilder.FILES_VALUE == clazz)
{
return createFiles(str);
}
else if (PatternOptionBuilder.URL_VALUE == clazz)
{
return createURL(str);
}
else
{
return null;
}
}
/**
* Create an Object from the classname and empty constructor.
*
* @param classname the argument value
* @return the initialised object
* @throws ParseException if the class could not be found or the object could not be created
*/
public static Object createObject(String classname) throws ParseException
{
Class<?> cl;
try
{
cl = Class.forName(classname);
}
catch (ClassNotFoundException cnfe)
{
throw new ParseException("Unable to find the class: " + classname);
}
try
{
return cl.newInstance();
}
catch (Exception e)
{
throw new ParseException(e.getClass().getName() + "; Unable to create an instance of: " + classname);
}
}
/**
* Create a number from a String. If a . is present, it creates a
* Double, otherwise a Long.
*
* @param str the value
* @return the number represented by <code>str</code>
* @throws ParseException if <code>str</code> is not a number
*/
public static Number createNumber(String str) throws ParseException
{
try
{
if (str.indexOf('.') != -1)
{
return Double.valueOf(str);
}
return Long.valueOf(str);
}
catch (NumberFormatException e)
{
throw new ParseException(e.getMessage());
}
}
/**
* Returns the class whose name is <code>classname</code>.
*
* @param classname the class name
* @return The class if it is found
* @throws ParseException if the class could not be found
*/
public static Class<?> createClass(String classname) throws ParseException
{
try
{
return Class.forName(classname);
}
catch (ClassNotFoundException e)
{
throw new ParseException("Unable to find the class: " + classname);
}
}
/**
* Returns the date represented by <code>str</code>.
* <p>
* This method is not yet implemented and always throws an
* {@link UnsupportedOperationException}.
*
* @param str the date string
* @return The date if <code>str</code> is a valid date string,
* otherwise return null.
* @throws UnsupportedOperationException always
*/
public static Date createDate(String str)
{
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* Returns the URL represented by <code>str</code>.
*
* @param str the URL string
* @return The URL in <code>str</code> is well-formed
* @throws ParseException if the URL in <code>str</code> is not well-formed
*/
public static URL createURL(String str) throws ParseException
{
try
{
return new URL(str);
}
catch (MalformedURLException e)
{
throw new ParseException("Unable to parse the URL: " + str);
}
}
/**
* Returns the File represented by <code>str</code>.
*
* @param str the File location
* @return The file represented by <code>str</code>.
*/
public static File createFile(String str)
{
return new File(str);
}
/**
* Returns the File[] represented by <code>str</code>.
* <p>
* This method is not yet implemented and always throws an
* {@link UnsupportedOperationException}.
*
* @param str the paths to the files
* @return The File[] represented by <code>str</code>.
* @throws UnsupportedOperationException always
*/
public static File[] createFiles(String str)
{
// to implement/port:
// return FileW.findFiles(str);
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@ -0,0 +1,71 @@
/**
* 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.commons.cli;
/**
* Exception thrown during parsing signalling an unrecognized
* option was seen.
*
* @version $Id: UnrecognizedOptionException.java 1443102 2013-02-06 18:12:16Z tn $
*/
public class UnrecognizedOptionException extends ParseException
{
/**
* This exception {@code serialVersionUID}.
*/
private static final long serialVersionUID = -252504690284625623L;
/** The unrecognized option */
private String option;
/**
* Construct a new <code>UnrecognizedArgumentException</code>
* with the specified detail message.
*
* @param message the detail message
*/
public UnrecognizedOptionException(String message)
{
super(message);
}
/**
* Construct a new <code>UnrecognizedArgumentException</code>
* with the specified option and detail message.
*
* @param message the detail message
* @param option the unrecognized option
* @since 1.2
*/
public UnrecognizedOptionException(String message, String option)
{
this(message);
this.option = option;
}
/**
* Returns the unrecognized option.
*
* @return the related option
* @since 1.2
*/
public String getOption()
{
return option;
}
}

View File

@ -0,0 +1,72 @@
/**
* 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.commons.cli;
/**
* Contains useful helper methods for classes within this package.
*
* @version $Id: Util.java 1443102 2013-02-06 18:12:16Z tn $
*/
final class Util
{
/**
* Remove the hyphens from the beginning of <code>str</code> and
* return the new String.
*
* @param str The string from which the hyphens should be removed.
*
* @return the new String.
*/
static String stripLeadingHyphens(String str)
{
if (str == null)
{
return null;
}
if (str.startsWith("--"))
{
return str.substring(2, str.length());
}
else if (str.startsWith("-"))
{
return str.substring(1, str.length());
}
return str;
}
/**
* Remove the leading and trailing quotes from <code>str</code>.
* E.g. if str is '"one two"', then 'one two' is returned.
*
* @param str The string from which the leading and trailing quotes
* should be removed.
*
* @return The string without the leading and trailing quotes.
*/
static String stripLeadingAndTrailingQuotes(String str)
{
int length = str.length();
if (length > 1 && str.startsWith("\"") && str.endsWith("\"") && str.substring(1, length - 1).indexOf('"') == -1)
{
str = str.substring(1, length - 1);
}
return str;
}
}

View File

@ -0,0 +1,43 @@
<!--
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.
-->
<body>
<p>Commons CLI -- version 1.3</p>
<p>The commons-cli package aides in parsing command-line arguments.</p>
<p>Allow command-line arguments to be parsed against a descriptor of
valid options (long and short), potentially with arguments.</p>
<p>command-line arguments may be of the typical <code>String[]</code>
form, but also may be a <code>java.util.List</code>. Indexes allow
for parsing only a portion of the command-line. Also, functionality
for parsing the command-line in phases is built in, allowing for
'cvs-style' command-lines, where some global options are specified
before a 'command' argument, and command-specific options are
specified after the command argument:
<code>
<pre>
myApp -p &lt;port&gt; command -p &lt;printer&gt;
</pre>
</code>
<p>The homepage for the project is
<a href="http://commons.apache.org">Apache Commons/</a>
</body>

View File

@ -0,0 +1,23 @@
/**
* 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.
*/
/**
* Commons CLI 1.3
*
* @version $Id: package-info.java 1443102 2013-02-06 18:12:16Z tn $
*/
package org.apache.commons.cli;

View File

@ -1,231 +0,0 @@
//TODO: fix
//package org.objectweb.asm.commons.cfg;
//
//import static org.objectweb.asm.Opcodes.ARETURN;
//import static org.objectweb.asm.Opcodes.ATHROW;
//import static org.objectweb.asm.Opcodes.DRETURN;
//import static org.objectweb.asm.Opcodes.FRETURN;
//import static org.objectweb.asm.Opcodes.GOTO;
//import static org.objectweb.asm.Opcodes.IRETURN;
//import static org.objectweb.asm.Opcodes.LRETURN;
//import static org.objectweb.asm.Opcodes.RETURN;
//
//import java.util.ArrayList;
//import java.util.Collections;
//import java.util.Comparator;
//import java.util.List;
//import java.util.Stack;
//
//import org.objectweb.asm.Label;
//import org.objectweb.asm.MethodVisitor;
//import org.objectweb.asm.commons.cfg.graph.Digraph;
//import org.objectweb.asm.tree.AbstractInsnNode;
//import org.objectweb.asm.tree.FieldInsnNode;
//import org.objectweb.asm.tree.IincInsnNode;
//import org.objectweb.asm.tree.InsnNode;
//import org.objectweb.asm.tree.IntInsnNode;
//import org.objectweb.asm.tree.InvokeDynamicInsnNode;
//import org.objectweb.asm.tree.JumpInsnNode;
//import org.objectweb.asm.tree.LabelNode;
//import org.objectweb.asm.tree.LdcInsnNode;
//import org.objectweb.asm.tree.LookupSwitchInsnNode;
//import org.objectweb.asm.tree.MethodInsnNode;
//import org.objectweb.asm.tree.MethodNode;
//import org.objectweb.asm.tree.MultiANewArrayInsnNode;
//import org.objectweb.asm.tree.TableSwitchInsnNode;
//import org.objectweb.asm.tree.TypeInsnNode;
//import org.objectweb.asm.tree.VarInsnNode;
//
///**
// * @author Tyler Sedlar
// */
//public class FlowVisitor extends MethodVisitor {
//
// private MethodNode mn;
// private Block current = new Block(new Label());
// public final List<Block> blocks = new ArrayList<>();
// public final Digraph<Block, Block> graph = new Digraph<>();
//
// public FlowVisitor() {
// super(Opcodes.ASM5);
// }
//
// public void accept(MethodNode mn) {
// current = new Block(new Label());
// blocks.clear();
// blocks.add(current);
// graph.flush();
// (this.mn = mn).accept(this);
// }
//
// /**
// * Constructs blocks for all given labels.
// *
// * @param labels The labels in which to construct blocks for.
// * @return The constructed blocks.
// */
// private Block[] constructAll(List<LabelNode> labels) {
// Block[] blocks = new Block[labels.size()];
// for (int i = 0; i < blocks.length; i++)
// blocks[i] = construct(labels.get(i));
// return blocks;
// }
//
// /**
// * Constructs a block for the given label.
// *
// * @param ln The label to get a block from.
// * @return The given label's block.
// */
// private Block construct(LabelNode ln) {
// return construct(ln, true);
// }
//
// /**
// * Constructs a block for the given label.
// *
// * @param ln The label to get a block from.
// * @param add <t>true</t> to add the block to the next preds, otherwise <t>false.</t>
// * @return A block for the given label.
// */
// private Block construct(LabelNode ln, boolean add) {
// Label label = ln.getLabel();
// if (!(label.info instanceof Block)) {
// label.info = new Block(label);
// if (add) {
// current.next = ((Block) label.info);
// current.next.preds.add(current.next);
// }
// blocks.add((Block) label.info);
// }
// return (Block) label.info;
// }
//
// @Override
// public void visitInsn(InsnNode in) {
// current.instructions.add(in);
// switch (in.opcode()) {
// case RETURN:
// case IRETURN:
// case ARETURN:
// case FRETURN:
// case DRETURN:
// case LRETURN:
// case ATHROW: {
// current = construct(new LabelNode(new Label()), false);
// break;
// }
// }
// }
//
// @Override
// public void visitIntInsn(IntInsnNode iin) {
// current.instructions.add(iin);
// }
//
// @Override
// public void visitVarInsn(VarInsnNode vin) {
// current.instructions.add(vin);
// }
//
// @Override
// public void visitTypeInsn(TypeInsnNode tin) {
// current.instructions.add(tin);
// }
//
// @Override
// public void visitFieldInsn(FieldInsnNode fin) {
// current.instructions.add(fin);
// }
//
// @Override
// public void visitMethodInsn(MethodInsnNode min) {
// current.instructions.add(min);
// }
//
// @Override
// public void visitInvokeDynamicInsn(InvokeDynamicInsnNode idin) {
// current.instructions.add(idin);
// }
//
// @Override
// public void visitJumpInsn(JumpInsnNode jin) {
// int opcode = jin.opcode();
// current.target = construct(jin.label);
// current.target.preds.add(current.target);
// if (opcode != GOTO)
// current.instructions.add(jin);
// Stack<AbstractInsnNode> stack = current.stack;
// current = construct(new LabelNode(new Label()), opcode != GOTO);
// current.stack = stack;
// }
//
// @Override
// public void visitLabel(Label label) {
// if (label == null || label.info == null) return;
// Stack<AbstractInsnNode> stack = current == null ? new Stack<AbstractInsnNode>() : current.stack;
// current = construct(new LabelNode(label));
// current.stack = stack;
// }
//
// @Override
// public void visitLdcInsn(LdcInsnNode ldc) {
// current.instructions.add(ldc);
// }
//
// @Override
// public void visitIincInsn(IincInsnNode iin) {
// current.instructions.add(iin);
// }
//
// @Override
// public void visitTableSwitchInsn(TableSwitchInsnNode tsin) {
// construct(tsin.dflt);
// constructAll(tsin.labels);
// current.instructions.add(tsin);
// }
//
// @Override
// public void visitLookupSwitchInsn(LookupSwitchInsnNode lsin) {
// construct(lsin.dflt);
// constructAll(lsin.labels);
// current.instructions.add(lsin);
// }
//
// @Override
// public void visitMultiANewArrayInsn(MultiANewArrayInsnNode manain) {
// current.instructions.add(manain);
// }
//
// @Override
// public void visitEnd() {
// List<Block> empty = new ArrayList<>();
// for (Block block : blocks) {
// block.owner = mn;
// if (block.isEmpty())
// empty.add(block);
// }
// blocks.removeAll(empty);
// Collections.sort(blocks, new Comparator<Block>() {
// @Override
// public int compare(Block b1, Block b2) {
// return mn.instructions.indexOf(new LabelNode(b1.label)) - mn.instructions.indexOf(new LabelNode(b2.label));
// }
// });
// for (Block block : blocks) {
// block.setIndex(blocks.indexOf(block));
// if (!graph.containsVertex(block))
// graph.addVertex(block);
// if (block.target != null && block.target != block) {
// if (!graph.containsVertex(block.target))
// graph.addVertex(block.target);
// graph.addEdge(block, block.target);
// }
// if (block.next != null) {
// if (!graph.containsVertex(block.next))
// graph.addVertex(block.next);
// graph.addEdge(block, block.next);
// }
// }
// }
//}

View File

@ -22,6 +22,7 @@ import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
@ -80,16 +81,14 @@ import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
* make it use that global last used inside of export as jar
* Spiffy up the plugin console with hilighted lines
* Take https://github.com/ptnkjke/Java-Bytecode-Editor visualize
* fix the randomly sometimes fucked up names on file navigation bug
* make zipfile not include the decode shit
* When you drag a folder, it must add the folder name not just the child into the root jtree path
* add stackmapframes to bytecode decompiler
* add stackmapframes remover?
* In BCV if you open a class and the name is so big, you cannot close because the [X] does not appear."
* refresh appears under panes that are non refreshable
* make ez-injection plugin console show all sys.out calls
* edit then save issues?
* Search open doesn't append .class to tab name
* Command line parameter to save source
* add JEB decompiler optionally, requires them to add jeb library jar externally and disable update check
* add decompile as zip for krakatau-bytecode and smali for CLI
*
* -----2.9.7-----:
* 07/02/2015 - Added ajustable font size.
@ -100,6 +99,14 @@ import the.bytecode.club.bytecodeviewer.plugin.PluginManager;
* 07/07/2015 - Finished the new Boot Screen
* 07/09/2015 - Fixed a process leak with krakatau decompiler.
* 07/09/2015 - Finished adding enjarify.
* 07/09/2015 - Supressed syntax exceptions due to JD-GUI.
* 07/09/2015 - Fixed refresh on non-refreshable resources.
* 07/09/2015 - Fixed opening a class and the name is so big, you cannot close because the [X] does not appear.
* 07/09/2015 - Added support for smaller screens for the boot screen.
* 07/16/2015 - Removed the FileFilter classes.
* 07/16/2015 - Updated the decompiler class to make more sense.
* 07/16/2015 - Started working on BCV CLI.
* 07/16/2015 - Finished BCV CLI.
*
* @author Konloch
*
@ -113,11 +120,11 @@ public class BytecodeViewer {
/*the rest*/
public static MainViewerGUI viewer = null;
public static ClassNodeLoader loader = new ClassNodeLoader(); //might be insecure due to assholes targeting BCV, however that's highly unlikely.
public static SecurityMan sm = new SecurityMan(); //might be insecure due to assholes targeting BCV, however that's highly unlikely.
public static String python = "";
public static String python3 = "";
public static String rt = "";
public static String library = "";
public static SecurityMan sm = new SecurityMan(); //might be insecure due to assholes targeting BCV, however that's highly unlikely.
public static HashMap<String, ClassNode> loadedClasses = new HashMap<String, ClassNode>();
public static HashMap<String, byte[]> loadedResources = new HashMap<String, byte[]>();
private static int maxRecentFiles = 25;
@ -219,7 +226,17 @@ public class BytecodeViewer {
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
fc.setFileFilter(viewer.new ZipFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(viewer);
@ -360,20 +377,21 @@ public class BytecodeViewer {
* @param args files you want to open
*/
public static void main(String[] args) {
System.out.println("https://the.bytecode.club - Created by @Konloch - Bytecode Viewer " + version);
System.setSecurityManager(sm);
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
if(previewCopy)
showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when 2.9.7 is actually out if you use this."+nl+
"Make sure to watch the repo: https://github.com/Konloch/bytecode-viewer for 2.9.7's release");
new BootScreen().DO_FIRST_BOOT(args);
if(previewCopy && !CommandLineInput.containsCommand(args))
showMessage("WARNING: This is a preview/dev copy, you WON'T be alerted when "+version+" is actually out if you use this."+nl+
"Make sure to watch the repo: https://github.com/Konloch/bytecode-viewer for "+version+"'s release");
new BootScreen().DO_FIRST_BOOT(args, CommandLineInput.parseCommandLine(args));
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
public static void BOOT(String[] args) {
System.out.println("https://the.bytecode.club - Created by @Konloch - Bytecode Viewer " + version);
public static void BOOT(String[] args, boolean cli) {
cleanup();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
@ -400,21 +418,16 @@ public class BytecodeViewer {
if(viewer.chckbxmntmNewCheckItem_12.isSelected())
versionChecker.start();
/*if (viewer.chckbxmntmNewCheckItem_12.isSelected()) // start only if selected
versionChecker.start();*/
viewer.setVisible(true);
if(!cli)
viewer.setVisible(true);
System.out.println("Start up took " + ((System.currentTimeMillis() - start) / 1000) + " seconds");
if (args.length >= 1)
for (String s : args) {
openFiles(new File[] { new File(s) }, true);
}
/*if(!pingback) {
pingback = true;
pingback();
}*/
if(!cli)
if (args.length >= 1)
for (String s : args) {
openFiles(new File[] { new File(s) }, true);
}
}
/**
@ -953,7 +966,27 @@ public class BytecodeViewer {
} catch(Exception e2) {
}
fc.setFileFilter(viewer.new APKDEXJarZipClassFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
if (extension.equals("jar") || extension.equals("zip")
|| extension.equals("class") || extension.equals("apk")
|| extension.equals("dex"))
return true;
return false;
}
@Override
public String getDescription() {
return "APKs, DEX, Class Files or Zip/Jar Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
@ -991,7 +1024,17 @@ public class BytecodeViewer {
if(viewer.autoCompileSmali.isSelected() && !BytecodeViewer.compile(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(viewer.new ZipFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(viewer);

View File

@ -0,0 +1,363 @@
package the.bytecode.club.bytecodeviewer;
import java.io.File;
import me.konloch.kontainer.io.DiskWriter;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.Options;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.decompilers.Decompiler;
/**
*
* Used to allow BCV to be integrated as CLI instead of GUI.
*
* @author Konloch
*
*/
public class CommandLineInput {
private static final Options options = new Options();
private static final CommandLineParser parser = new DefaultParser();
static {
options.addOption("help", false, "prints the help menu.");
options.addOption("list", false, "lists all the available decompilers for BCV " + BytecodeViewer.version+".");
options.addOption("decompiler", true, "sets the decompiler, procyon by default.");
options.addOption("i", true, "sets the input.");
options.addOption("o", true, "sets the output.");
options.addOption("t", true, "sets the target class to decompile, append all to decomp all as zip.");
options.addOption("nowait", true, "won't wait the 5 seconds to allow the user to read the CLI.");
}
public static boolean containsCommand(String[] args) {
if(args == null || args.length == 0)
return false;
try {
CommandLine cmd = parser.parse(options, args);
if(
cmd.hasOption("help") ||
cmd.hasOption("list") ||
cmd.hasOption("decompiler") ||
cmd.hasOption("i") ||
cmd.hasOption("o") ||
cmd.hasOption("t") ||
cmd.hasOption("nowait")
) {
return true;
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
public static int parseCommandLine(String[] args) {
if(!containsCommand(args))
return 1;
try {
CommandLine cmd = parser.parse(options, args);
if(cmd.hasOption("list")) {
System.out.println("Procyon");
System.out.println("CFR");
System.out.println("FernFlower");
System.out.println("Krakatau");
System.out.println("Krakatau-Bytecode");
System.out.println("JD-GUI");
System.out.println("Smali");
return -1;
} else if(cmd.hasOption("help")) {
for(String s : new String[] {
"-help Displays the help menu",
"-list Displays the available decompilers",
"-decompiler <decompiler> Selects the decompiler, procyon by default",
"-i <input file> Selects the input file",
"-o <output file> Selects the output file",
"-nowait Doesn't wait for the user to read the CLI messages"
})
System.out.println(s);
return -1;
} else {
if(cmd.getOptionValue("i") == null) {
System.err.println("Set the input with -i");
return -1;
} if(cmd.getOptionValue("o") == null) {
System.err.println("Set the output with -o");
return -1;
} if(cmd.getOptionValue("t") == null) {
System.err.println("Set the target with -t");
return -1;
}
File input = new File(cmd.getOptionValue("i"));
File output = new File(cmd.getOptionValue("o"));
String decompiler = cmd.getOptionValue("decompiler");
if(!input.exists()) {
System.err.println(input.getAbsolutePath() + " does not exist.");
return -1;
}
if(output.exists()) {
System.err.println("WARNING: Deleted old " + output.getAbsolutePath() + ".");
output.delete();
}
//check if zip, jar, apk, dex, or class
//if its zip/jar/apk/dex attempt unzip as whole zip
//if its just class allow any
if(
decompiler != null &&
!decompiler.equalsIgnoreCase("procyon") &&
!decompiler.equalsIgnoreCase("cfr") &&
!decompiler.equalsIgnoreCase("fernflower") &&
!decompiler.equalsIgnoreCase("krakatau") &&
!decompiler.equalsIgnoreCase("krakatau-bytecode") &&
!decompiler.equalsIgnoreCase("jd-gui") &&
!decompiler.equalsIgnoreCase("smali")
) {
System.out.println("Error, no decompiler called '" + decompiler + "' found. Type -decompiler-list for the list");
}
if(!cmd.hasOption("nowait"))
Thread.sleep(5 * 1000);
return 0;
}
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
return 1;
}
public static void executeCommandLine(String[] args) {
try {
CommandLine cmd = parser.parse(options, args);
String decompiler = cmd.getOptionValue("decompiler");
File input = new File(cmd.getOptionValue("i"));
File output = new File(cmd.getOptionValue("o"));
String target = cmd.getOptionValue("t");
if(cmd.getOptionValue("decompiler") == null) {
System.out.println("You can define another decompiler by appending -decompiler \"name\", by default procyon has been set.");
decompiler = "procyon";
}
//check if zip, jar, apk, dex, or class
//if its zip/jar/apk/dex attempt unzip as whole zip
//if its just class allow any
if(decompiler.equalsIgnoreCase("procyon")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with Procyon");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
Decompiler.procyon.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.procyon.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("cfr")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with CFR");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
Decompiler.cfr.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.cfr.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("fernflower")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with FernFlower");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
Decompiler.fernflower.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.fernflower.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("krakatau")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with Krakatau");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
Decompiler.krakatau.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.krakatau.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("krakatau-bytecode")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with Krakatau-Bytecode");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
System.out.println("Coming soon.");
//Decompiler.krakatauDA.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.krakatauDA.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("jd-gui")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with JD-GUI");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
Decompiler.jdgui.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.jdgui.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
} else if(decompiler.equalsIgnoreCase("smali")) {
System.out.println("Decompiling " + input.getAbsolutePath() + " with Smali");
BytecodeViewer.openFiles(new File[]{input}, false);
Thread.sleep(5 * 1000);
if(target.equalsIgnoreCase("all")) {
System.out.println("Coming soon.");
//Decompiler.smali.decompileToZip(output.getAbsolutePath());
} else {
try {
ClassNode cn = BytecodeViewer.getClassNode(target);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.smali.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(output.getAbsolutePath(), contents, false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI( e);
}
}
}
System.out.println("Finished.");
System.out.println("Bytecode Viewer CLI v" + BytecodeViewer.version + " by @Konloch - http://bytecodeviewer.com");
System.exit(0);
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
}
}

View File

@ -83,4 +83,11 @@ public class MiscUtils {
public static String extension(String name) {
return name.substring(name.lastIndexOf('.') + 1);
}
public static String append(File file, String extension) {
String path = file.getAbsolutePath();
if (!path.endsWith(extension))
path = path + extension;
return path;
}
}

View File

@ -13,7 +13,7 @@ import java.security.Permission;
public class SecurityMan extends SecurityManager {
public boolean blocking = true;
public boolean blocking = true; //might be insecure due to assholes targeting BCV, however that's highly unlikely.
@Override
public void checkExec(String cmd) {
if(blocking)

View File

@ -16,9 +16,6 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -33,23 +30,6 @@ import the.bytecode.club.bytecodeviewer.MiscUtils;
*/
public class CFRDecompiler extends Decompiler {
@Override
public void decompileToClass(String className, String classNameSaved) {
ClassNode cn = BytecodeViewer.getClassNode(className);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(classNameSaved, contents, false);
}
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {

View File

@ -13,18 +13,18 @@ import the.bytecode.club.bytecodeviewer.decompilers.bytecode.ClassNodeDecompiler
public abstract class Decompiler {
public final static Decompiler bytecode = new ClassNodeDecompiler();
public final static Decompiler fernflower = new FernFlowerDecompiler();
public final static Decompiler procyon = new ProcyonDecompiler();
public final static Decompiler cfr = new CFRDecompiler();
public final static Decompiler krakatau = new KrakatauDecompiler();
public final static Decompiler krakatauDA = new KrakatauDisassembler();
public final static Decompiler smali = new SmaliDisassembler();
public final static Decompiler jdgui = new JDGUIDecompiler();
public abstract String decompileClassNode(ClassNode cn, byte[] b);
public abstract void decompileToZip(String zipName);
public abstract void decompileToClass(String className, String classNameSaved);
public static Decompiler bytecode = new ClassNodeDecompiler();
public static Decompiler fernflower = new FernFlowerDecompiler();
public static Decompiler procyon = new ProcyonDecompiler();
public static Decompiler cfr = new CFRDecompiler();
public static Decompiler krakatau = new KrakatauDecompiler();
public static Decompiler krakatauDA = new KrakatauDisassembler();
public static Decompiler smali = new SmaliDisassembler();
public static Decompiler jdgui = new JDGUIDecompiler();
}

View File

@ -7,9 +7,7 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -26,23 +24,6 @@ import the.bytecode.club.bytecodeviewer.MiscUtils;
public class FernFlowerDecompiler extends Decompiler {
@Override
public void decompileToClass(String className, String classNameSaved) {
ClassNode cn = BytecodeViewer.getClassNode(className);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(classNameSaved, contents, false);
}
@Override
public void decompileToZip(String zipName) {
File tempZip = new File(BytecodeViewer.tempDirectory + "temp.zip");

View File

@ -12,9 +12,7 @@ import jd.cli.preferences.CommonPreferences;
import jd.cli.util.ClassFileUtil;
import jd.core.process.DecompilerImpl;
import me.konloch.kontainer.io.DiskReader;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -30,23 +28,6 @@ import jd.cli.printer.text.PlainTextPrinter;
*/
public class JDGUIDecompiler extends Decompiler {
@Override
public void decompileToClass(String className, String classNameSaved) {
ClassNode cn = BytecodeViewer.getClassNode(className);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(classNameSaved, contents, false);
}
@Override
public String decompileClassNode(ClassNode cn, byte[] b) {

View File

@ -14,6 +14,7 @@ import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.JarUtils;
import the.bytecode.club.bytecodeviewer.MiscUtils;
import the.bytecode.club.bytecodeviewer.ZipUtils;
/**
* Krakatau Java Disassembler Wrapper, requires Python 2.7
@ -92,6 +93,64 @@ public class KrakatauDisassembler extends Decompiler {
return s;
}
@Override public void decompileToZip(String zipName) { }
@Override public void decompileToClass(String className, String classNameSaved) { }
@Override public void decompileToZip(String zipName) {
if(BytecodeViewer.python.equals("")) {
BytecodeViewer.showMessage("You need to set your Python (or PyPy for speed) 2.7 executable path.");
BytecodeViewer.viewer.pythonC();
}
String ran = MiscUtils.randomString(32);
final File tempDirectory = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + ran + BytecodeViewer.fs);
tempDirectory.mkdir();
final File tempJar = new File(BytecodeViewer.tempDirectory + BytecodeViewer.fs + "temp.jar");
JarUtils.saveAsJar(BytecodeViewer.getLoadedClasses(), tempJar.getAbsolutePath());
BytecodeViewer.sm.blocking = false;
try {
ProcessBuilder pb = new ProcessBuilder(
BytecodeViewer.python,
"-O", //love you storyyeller <3
BytecodeViewer.krakatauWorkingDirectory + BytecodeViewer.fs + "disassemble.py",
"-path",
BytecodeViewer.rt+";"+tempJar.getAbsolutePath(),
"-out",
tempDirectory.getAbsolutePath(),
tempJar.getAbsolutePath()
);
Process process = pb.start();
BytecodeViewer.krakatau.add(process);
//Read out dir output
InputStream is = process.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
is = process.getErrorStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
while ((line = br.readLine()) != null) {
System.out.println(line);
}
br.close();
int exitValue = process.waitFor();
System.out.println("Exit Value is " + exitValue);
// ZipUtils.zipDirectory(tempDirectory, new File(zipName));
ZipUtils.zipFolder(tempDirectory.getAbsolutePath(), zipName, ran);
//tempDirectory.delete();
tempJar.delete();
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
}
BytecodeViewer.sm.blocking = true;
}
}

View File

@ -19,9 +19,6 @@ import java.util.jar.JarFile;
import java.util.zip.ZipException;
import java.util.zip.ZipOutputStream;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import com.strobel.core.StringUtilities;
@ -51,23 +48,6 @@ import the.bytecode.club.bytecodeviewer.MiscUtils;
public class ProcyonDecompiler extends Decompiler {
@Override
public void decompileToClass(String className, String classNameSaved) {
ClassNode cn = BytecodeViewer.getClassNode(className);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(classNameSaved, contents, false);
}
public DecompilerSettings getDecompilerSettings() {
DecompilerSettings settings = new DecompilerSettings();
settings.setAlwaysGenerateExceptionVariableForCatchBlocks(BytecodeViewer.viewer.chckbxmntmNewCheckItem_6

View File

@ -80,6 +80,7 @@ public class SmaliDisassembler extends Decompiler {
return null;
}
@Override public void decompileToZip(String zipName) { }
@Override public void decompileToClass(String className, String classNameSaved) { }
@Override public void decompileToZip(String zipName) {
}
}

View File

@ -138,5 +138,4 @@ public class ClassNodeDecompiler extends Decompiler {
}
@Override public void decompileToZip(String zipName) { }
@Override public void decompileToClass(String className, String classNameSaved) { }
}

View File

@ -60,7 +60,7 @@ public class AboutWindow extends JFrame {
"CTRL + T: Compile"+BytecodeViewer.nl+
"CTRL + S: Save classes as zip"+BytecodeViewer.nl+
"CTRL + R: Run (EZ-Inject) - dynamically load the classes and invoke a main class"+
"\r\n\r\nIt uses code from the following:\r\n J-RET by WaterWolf\r\n JHexPane by Sam Koivu\r\n RSynaxPane by Robert Futrell\r\n Commons IO by Apache\r\n ASM by OW2\r\n FernFlower by Stiver\r\n Procyon by Mstrobel\r\n CFR by Lee Benfield\r\n CFIDE by Bibl\r\n Smali by JesusFreke\r\n Dex2Jar by pxb1..?\r\n Krakatau by Storyyeller\r\n JD-GUI + JD-Core by The Java-Decompiler Team\r\n\r\nIf you're interested in Java Reverse Engineering, join The Bytecode Club\r\nhttps://the.bytecode.club");
"\r\n\r\nCode from various projects has been used, including but not limited to:\r\n J-RET by WaterWolf\r\n JHexPane by Sam Koivu\r\n RSynaxPane by Robert Futrell\r\n Commons IO by Apache\r\n ASM by OW2\r\n FernFlower by Stiver\r\n Procyon by Mstrobel\r\n CFR by Lee Benfield\r\n CFIDE by Bibl\r\n Smali by JesusFreke\r\n Dex2Jar by pxb1..?\r\n Krakatau by Storyyeller\r\n JD-GUI + JD-Core by The Java-Decompiler Team\r\n Enjarify by Storyyeller\r\n\r\nIf you're interested in Java Reverse Engineering, join The Bytecode Club\r\nhttps://the.bytecode.club");
}

View File

@ -5,6 +5,7 @@ import javax.swing.JFrame;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import javax.swing.JProgressBar;
@ -33,19 +34,14 @@ import javax.swing.text.html.HTMLEditorKit;
import org.apache.commons.io.FileUtils;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
import the.bytecode.club.bytecodeviewer.CommandLineInput;
import the.bytecode.club.bytecodeviewer.Resources;
import the.bytecode.club.bytecodeviewer.Settings;
import the.bytecode.club.bytecodeviewer.ZipUtils;
import me.konloch.kontainer.io.HTTPRequest;
/**
* First boot, will automatically connect to BytecodeViewer for PingBack
* It'll Check BCV version
* then it'll download repos from the library
* After it's completed and compared MD5 hashes, it simply dynamically loads all jars in /libs/ folder of BCV
* While all of this is happening, it'll show the HOW-TO guide for BCV
*
* Download Failed? Corrupt Jar? Append -clean to BCV startup
* Automatic updater for BCV libraries
*
* @author Konloch
*
@ -62,7 +58,17 @@ public class BootScreen extends JFrame {
public BootScreen() throws IOException {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setIconImages(Resources.iconList);
setSize(new Dimension(600, 800));
int i = (int)Toolkit.getDefaultToolkit().getScreenSize().getHeight();
if(i >= 840)
setSize(new Dimension(600, 800));
else if(i >= 640)
setSize(new Dimension(500, 600));
else if(i >= 440)
setSize(new Dimension(400, 400));
else
setSize(Toolkit.getDefaultToolkit().getScreenSize());
setTitle("Bytecode Viewer Boot Screen - Starting Up");
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[]{0, 0};
@ -104,8 +110,13 @@ public class BootScreen extends JFrame {
return string;
}
public void DO_FIRST_BOOT(String args[]) {
this.setVisible(true);
public void DO_FIRST_BOOT(String args[], int CLI) {
if(CLI == -1)
return;
if(CLI == 1)
this.setVisible(true);
if(FIRST_BOOT)
return;
@ -114,6 +125,8 @@ public class BootScreen extends JFrame {
setTitle("Bytecode Viewer Boot Screen - Checking Libraries...");
System.out.println("Checking Libraries...");
File libsDirectory = new File(BytecodeViewer.libsDirectory);
try {
@ -151,6 +164,7 @@ public class BootScreen extends JFrame {
String fileName = s.substring("https://github.com/Konloch/bytecode-viewer/blob/master/libs/".length(), s.length());
if(!libsList.contains(fileName)) {
setTitle("Bytecode Viewer Boot Screen - Downloading " + fileName);
System.out.println("Downloading " + fileName);
boolean passed = false;
while(!passed) {
InputStream is = null;
@ -199,7 +213,8 @@ public class BootScreen extends JFrame {
}
if(BytecodeViewer.deleteForiegnLibraries) {
setTitle("Bytecode Viewer Boot Screen - Checking & Deleting Foriegn/Outdated Libraries...");
setTitle("Bytecode Viewer Boot Screen - Checking & Deleting Foreign/Outdated Libraries...");
System.out.println("Checking & Deleting foreign/outdated libraries");
for(String s : libsFileList) {
File f = new File(s);
boolean delete = true;
@ -216,12 +231,14 @@ public class BootScreen extends JFrame {
}
setTitle("Bytecode Viewer Boot Screen - Loading Libraries...");
System.out.println("Loading libraries...");
for(String s : libsFileList ) {
if(s.endsWith(".jar")) {
File f = new File(s);
if(f.exists()) {
setTitle("Bytecode Viewer Boot Screen - Loading Library " + f.getName());
System.out.println("Loading library " + f.getName());
try {
JarFile jarFile = new JarFile(s);
@ -256,6 +273,8 @@ public class BootScreen extends JFrame {
setTitle("Bytecode Viewer Boot Screen - Checking Krakatau...");
System.out.println("Checking krakatau");
File krakatauZip = null;
for(File f : new File(BytecodeViewer.libsDirectory).listFiles()) {
if(f.getName().toLowerCase().startsWith("krakatau-")) {
@ -296,6 +315,8 @@ public class BootScreen extends JFrame {
setTitle("Bytecode Viewer Boot Screen - Checking Enjarify...");
System.out.println("Checking enjarify");
File enjarifyZip = null;
for(File f : new File(BytecodeViewer.libsDirectory).listFiles()) {
if(f.getName().toLowerCase().startsWith("enjarify-")) {
@ -346,8 +367,13 @@ public class BootScreen extends JFrame {
setTitle("Bytecode Viewer Boot Screen - Finished");
BytecodeViewer.BOOT(args);
if(CLI == 1)
BytecodeViewer.BOOT(args, false);
else {
BytecodeViewer.BOOT(args, true);
CommandLineInput.executeCommandLine(args);
}
this.setVisible(false);
}

View File

@ -831,6 +831,8 @@ public class ClassViewer extends Viewer {
java1 = panelArea;
}
} catch(java.lang.IndexOutOfBoundsException | java.lang.NullPointerException e) {
//ignore
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {
@ -1071,6 +1073,8 @@ public class ClassViewer extends Viewer {
java2 = panelArea;
}
} catch(java.lang.IndexOutOfBoundsException | java.lang.NullPointerException e) {
//ignore
} catch(Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {
@ -1312,6 +1316,8 @@ public class ClassViewer extends Viewer {
panelArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, (int)BytecodeViewer.viewer.fontSpinner.getValue()));
}
} catch(java.lang.IndexOutOfBoundsException | java.lang.NullPointerException e) {
//ignore
} catch(Exception e) {
//new the.bytecode.club.bytecodeviewer.api.ExceptionUI(e);
} finally {

View File

@ -55,6 +55,7 @@ public class FileViewer extends Viewer {
public JCheckBox check = new JCheckBox("Exact");
final JTextField field = new JTextField();
public BufferedImage image;
boolean canRefresh = false;
public void setContents() {
String name = this.name.toLowerCase();
@ -79,6 +80,7 @@ public class FileViewer extends Viewer {
if( name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") ||
name.endsWith(".gif") || name.endsWith(".tif") || name.endsWith(".bmp"))
{
canRefresh = true;
try {
image = ImageIO.read(new ByteArrayInputStream(contents)); //gifs fail cause of this
JLabel label = new JLabel("", new ImageIcon(image), JLabel.CENTER);
@ -364,6 +366,11 @@ public class FileViewer extends Viewer {
}
public void refresh(JButton src) {
if(!canRefresh) {
src.setEnabled(true);
return;
}
panel2.removeAll();
try {
image = ImageIO.read(new ByteArrayInputStream(contents));

View File

@ -30,6 +30,9 @@ import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import me.konloch.kontainer.io.DiskWriter;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.ClassNode;
import the.bytecode.club.bytecodeviewer.BytecodeViewer;
@ -64,7 +67,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public void pythonC() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new PythonCFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "Python (Or PyPy for speed) 2.7 Executable";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
@ -79,7 +92,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public void pythonC3() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new PythonC3FileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "Python (Or PyPy for speed) 3.x Executable";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
@ -94,7 +117,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public void library() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new LibraryFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory();
}
@Override
public String getDescription() {
return "Optional Library Folder";
}
});
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
@ -111,7 +144,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
public void rtC() {
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new RTCFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "JRE RT Library";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
@ -596,7 +639,27 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
} catch(Exception e2) {
}
fc.setFileFilter(new APKDEXJarZipClassFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
if (extension.equals("jar") || extension.equals("zip")
|| extension.equals("class") || extension.equals("apk")
|| extension.equals("dex"))
return true;
return false;
}
@Override
public String getDescription() {
return "APKs, DEX, Class Files or Zip/Jar Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showOpenDialog(BytecodeViewer.viewer);
@ -630,7 +693,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
if(autoCompileSmali.isSelected() && !BytecodeViewer.compile(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
@ -687,7 +760,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
if(autoCompileSmali.isSelected() && !BytecodeViewer.compile(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new JarFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
@ -756,7 +839,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
if(autoCompileSmali.isSelected() && !BytecodeViewer.compile(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new DexFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("dex");
}
@Override
public String getDescription() {
return "Android DEX Files";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
@ -827,7 +920,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
if(autoCompileSmali.isSelected() && !BytecodeViewer.compile(false))
return;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new ZipFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
@ -858,8 +961,8 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
}
BytecodeViewer.viewer.setIcon(true);
final String path = appendZip(file);// cheap hax cause
// string is final
final String path = MiscUtils.append(file, ".zip"); // cheap hax cause
// string is final
JOptionPane pane = new JOptionPane(
"What decompiler will you use?");
@ -951,7 +1054,17 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
final String s = workPane.getCurrentViewer().name;
JFileChooser fc = new JFileChooser();
fc.setFileFilter(new JavaFileFilter());
fc.setFileFilter(new FileFilter() {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("java");
}
@Override
public String getDescription() {
return "Java Source Files";
}
});
fc.setFileHidingEnabled(false);
fc.setAcceptAllFileFilterUsed(false);
int returnVal = fc.showSaveDialog(MainViewerGUI.this);
@ -959,8 +1072,8 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
File file = fc.getSelectedFile();
BytecodeViewer.viewer.setIcon(true);
final String path = appendJava(file);// cheap hax cause
// string is final
final String path = MiscUtils.append(file, ".java"); // cheap hax cause
// string is final
if(new File(path).exists()) {
JOptionPane pane = new JOptionPane(
@ -1002,7 +1115,19 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@Override
public void run() {
try {
Decompiler.procyon.decompileToClass(s,path);
ClassNode cn = BytecodeViewer.getClassNode(s);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.procyon.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(
@ -1017,7 +1142,19 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@Override
public void run() {
try {
Decompiler.cfr.decompileToClass(s,path);
ClassNode cn = BytecodeViewer.getClassNode(s);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.cfr.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(
@ -1032,7 +1169,20 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@Override
public void run() {
try {
Decompiler.fernflower.decompileToClass(s,path);
ClassNode cn = BytecodeViewer.getClassNode(s);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.fernflower.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(
@ -1047,7 +1197,19 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
@Override
public void run() {
try {
Decompiler.krakatau.decompileToClass(s,path);
ClassNode cn = BytecodeViewer.getClassNode(s);
final ClassWriter cw = new ClassWriter(0);
try {
cn.accept(cw);
} catch(Exception e) {
e.printStackTrace();
try {
Thread.sleep(200);
cn.accept(cw);
} catch (InterruptedException e1) { }
}
String contents = Decompiler.krakatau.decompileClassNode(cn, cw.toByteArray());
DiskWriter.replaceFile(path, contents, false);
BytecodeViewer.viewer.setIcon(false);
} catch (Exception e) {
new the.bytecode.club.bytecodeviewer.api.ExceptionUI(
@ -1746,7 +1908,11 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
});
setSize(new Dimension(800, 400));
setTitle("Bytecode Viewer "+BytecodeViewer.version+" - https://bytecodeviewer.com | https://the.bytecode.club - @Konloch");
if(BytecodeViewer.previewCopy)
setTitle("Bytecode Viewer "+BytecodeViewer.version+" preview - https://bytecodeviewer.com | https://the.bytecode.club - @Konloch");
else
setTitle("Bytecode Viewer "+BytecodeViewer.version+" - https://bytecodeviewer.com | https://the.bytecode.club - @Konloch");
getContentPane().setLayout(
new BoxLayout(getContentPane(), BoxLayout.X_AXIS));
@ -1824,26 +1990,6 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
this.setLocationRelativeTo(null);
}
public String appendZip(File file) {
String path = file.getAbsolutePath();
if (!path.endsWith(".zip"))
path = path + ".zip";
return path;
}
public String appendClass(File file) {
String path = file.getAbsolutePath();
if (!path.endsWith(".class"))
path = path + ".class";
return path;
}
public String appendJava(File file) {
String path = file.getAbsolutePath();
if (!path.endsWith(".java"))
path = path + ".java";
return path;
}
@Override
public void openClassFile(final String name, final ClassNode cn) {
@ -1867,164 +2013,4 @@ public class MainViewerGUI extends JFrame implements FileChangeNotifier {
}
return null;
}
/* 01/06/15, 14:33 changed by Bibl. */
public class GroovyPythonRubyFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
return (extension.equals("gy") || extension.equals("groovy")
|| extension.equals("py") || extension.equals("python")
|| extension.equals("rb") || extension.equals("ruby"));
return false;
}
@Override
public String getDescription() {
return "Groovy, Python or Ruby plugins.";
}
}
public class GroovyFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
return (extension.equals("gy") || extension.equals("groovy"));
return false;
}
@Override
public String getDescription() {
return "Groovy plugins.";
}
}
public class APKDEXJarZipClassFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
if (f.isDirectory())
return true;
String extension = MiscUtils.extension(f.getAbsolutePath());
if (extension != null)
if (extension.equals("jar") || extension.equals("zip")
|| extension.equals("class") || extension.equals("apk")
|| extension.equals("dex"))
return true;
return false;
}
@Override
public String getDescription() {
return "APKs, DEX, Class Files or Zip/Jar Archives";
}
}
public class ZipFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("zip");
}
@Override
public String getDescription() {
return "Zip Archives";
}
}
public class JarFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("jar");
}
@Override
public String getDescription() {
return "Jar Archives";
}
}
public class JavaFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("java");
}
@Override
public String getDescription() {
return "Java Source Files";
}
}
public class DexFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory() || MiscUtils.extension(f.getAbsolutePath()).equals("dex");
}
@Override
public String getDescription() {
return "Android DEX Files";
}
}
public class PythonCFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "Python (Or PyPy for speed) 2.7 Executable";
}
}
public class PythonC3FileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "Python (Or PyPy for speed) 3.x Executable";
}
}
public class RTCFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return true;
}
@Override
public String getDescription() {
return "JRE RT Library";
}
}
public class LibraryFileFilter extends FileFilter {
@Override
public boolean accept(File f) {
return f.isDirectory();
}
@Override
public String getDescription() {
return "Optional Library Folder";
}
}
}

View File

@ -59,6 +59,15 @@ public class TabbedPane extends JPanel {
return pane.getTitleAt(i);
return null;
}
@Override
public Dimension getPreferredSize() {
Dimension realDimension = super.getPreferredSize();
if(realDimension.getWidth() >= 400)
return new Dimension(400, 20);
else
return realDimension;
}
};
this.add(label);