/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*
* Copyright 2006 - 2016 Pentaho Corporation. All rights reserved.
*/
package org.pentaho.aggdes;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.ListIterator;
import java.util.Map;
import org.pentaho.aggdes.algorithm.Algorithm;
import org.pentaho.aggdes.algorithm.Progress;
import org.pentaho.aggdes.algorithm.Result;
import org.pentaho.aggdes.algorithm.util.ArgumentUtils;
import org.pentaho.aggdes.algorithm.util.ArgumentUtils.ValidationException;
import org.pentaho.aggdes.model.Component;
import org.pentaho.aggdes.model.Parameter;
import org.pentaho.aggdes.model.Schema;
import org.pentaho.aggdes.model.SchemaLoader;
import org.pentaho.aggdes.output.ResultHandler;
/**
* Command line utility to run the aggregate designer algorithm.
*
* <p>Example usage:
*
* <blockquote>
* <pre>
* java org.pentaho.aggdes.algorithm.Main
* --loaderClass org.pentaho.aggdes.model.mondrian.MondrianSchemaLoader
* --loaderParam connectString 'Provider=mondrian;Jdbc=jdbc:odbc:MondrianFoodMart;Catalog=/open/mondrian/demo/FoodMart.xml;JdbcDrivers=sun.jdbc.odbc.JdbcOdbcDriver'
* --loaderParam cube Sales
* --algorithmClass org.pentaho.aggdes.algorithm.impl.AdaptiveMonteCarloAlgorithm
* --algorithmParam timeLimitSeconds 300
* --algorithmParam aggregateLimit 10
* --resultClass org.pentaho.aggdes.output.impl.ResultHandlerImpl
* --resultParam tables true
* --resultParam indexes false
* --resultParam populate true
* </pre>
* </blockquote>
*
* The components:
* <ul>
* <li>The loader is a class that implements the {@link SchemaLoader}
* interface. Example: MondrianSchemaLoader.
* <li>The algorithm is a class that implements the {@link Algorithm}
* interface. Examples: AdaptiveMonteCarloAlgorithm, MonteCarloAlgorithm,
* ExhaustiveLatticeAlgorithm
* </ul>
*
* @author jhyde
* @version $Id: Main.java 85 2008-04-28 22:20:13Z jhyde $
* @since Mar 13, 2008
*/
public class Main {
private final PrintWriter pw = new PrintWriter(System.out);
private final String[] args;
private SchemaLoader loader;
private Algorithm algorithm;
private ResultHandler resultHandler;
private final Map<String, String> loaderRawParams =
new LinkedHashMap<String, String>();
private final Map<String, String> algorithmRawParams =
new LinkedHashMap<String, String>();
private final Map<String, String> resultHandlerRawParams =
new LinkedHashMap<String, String>();
/**
* Creates an instance of Main with the command-line parameters.
*
* @param args Command-line parameters
*/
private Main(String[] args) {
this.args = args;
}
public static void main(String[] args) {
final Main main = new Main(args);
try {
main.run();
} finally {
main.pw.flush();
}
}
/**
* Prints usage.
*
* @param component Component; if not null, describes the parameters
* accepted by given component
*/
private void usage(Component component) {
pw.println("Usage: java " + Main.class.getName());
pw.println(" --help");
pw.println(" --loaderClass <class>");
pw.println(" [ --loaderParam <name> <value> ]...");
pw.println(" --algorithmClass <class>");
pw.println(" [ --algorithmParam <name> <value> ]...");
pw.println(" --resultClass <class>");
pw.println(" [ --resultParam <name> <value> ]...");
if (component != null) {
pw.println();
pw.println("Parameters for component " + component.getName()
+ " are:");
for (Parameter parameter : component.getParameters()) {
String desc = " " + parameter.getName()
+ " (" + parameter.getType()
+ (parameter.isRequired() ? ", required" : "")
+ ") " + parameter.getDescription();
pw.println(desc);
}
}
}
private void parseArgs() {
assert loader == null;
assert algorithm == null;
assert loaderRawParams.isEmpty();
assert algorithmRawParams.isEmpty();
ListIterator<String> argIter = Arrays.asList(args).listIterator();
while (argIter.hasNext()) {
String arg = argIter.next();
if (arg.equals("--loaderClass")) {
loader = readComponentClass(argIter, SchemaLoader.class);
} else if (arg.equals("--loaderParam")) {
readParam(arg, argIter, loaderRawParams, loader);
} else if (arg.equals("--algorithmClass")) {
algorithm = readComponentClass(argIter, Algorithm.class);
} else if (arg.equals("--algorithmParam")) {
readParam(arg, argIter, algorithmRawParams, algorithm);
} else if (arg.equals("--resultClass")) {
resultHandler = readComponentClass(argIter, ResultHandler.class);
} else if (arg.equals("--resultParam")) {
readParam(arg, argIter, resultHandlerRawParams, resultHandler);
} else {
throw new ValidationException(
null,
"Unknown parameter '" + arg + "'");
}
}
if (loader == null) {
throw new ValidationException(
null,
"Missing required component. "
+ "Please specify '--loaderClass' argument");
}
if (algorithm == null) {
throw new ValidationException(
null,
"Missing required component. "
+ "Please specify '--algorithmClass' argument");
}
if (resultHandler == null) {
throw new ValidationException(
null,
"Missing required component. "
+ "Please specify '--resultClass' argument");
}
}
private void readParam(
String argName,
ListIterator<String> argIter,
Map<String, String> params,
Component component)
{
if (argIter.hasNext()) {
String paramName = argIter.next();
if (argIter.hasNext()) {
String paramValue = argIter.next();
params.put(paramName, paramValue);
return;
}
}
throw new ValidationException(
component,
"Expected arguments <name> <value> following " + argName);
}
private <T> T readComponentClass(
ListIterator<String> argIter,
Class<T> iface)
{
if (!argIter.hasNext()) {
throw new ValidationException(
null,
"Expected argument <className>");
}
String arg = argIter.next();
try {
final Class<?> clazz = Class.forName(arg);
if (!iface.isAssignableFrom(clazz)) {
throw new ValidationException(
null,
"Class '" + arg
+ "' does not implement required interface '"
+ iface.getName() + "'");
}
final Object o = clazz.newInstance();
return iface.cast(o);
} catch (ClassNotFoundException e) {
throw new ValidationException(
null,
"Class '" + arg + "' not found");
} catch (IllegalAccessException e) {
throw new ValidationException(
null,
"Error while instantiating class '" + arg + "'");
} catch (InstantiationException e) {
throw new ValidationException(
null,
"Error while instantiating class '" + arg + "'");
}
}
void run() {
if (args.length == 0) {
usage(null);
return;
}
Map<Parameter, Object> loaderParams;
Map<Parameter, Object> algorithmParams;
Map<Parameter, Object> resultHandlerParams;
try {
parseArgs();
// Validate all parameters up front, so we fail fast.
loaderParams =
ArgumentUtils.validateParameters(loader, loaderRawParams);
algorithmParams =
ArgumentUtils.validateParameters(algorithm, algorithmRawParams);
resultHandlerParams =
ArgumentUtils.validateParameters(resultHandler, resultHandlerRawParams);
} catch (ValidationException e) {
pw.println(e.getMessage());
usage(e.getComponent());
return;
} catch (RuntimeException e) {
usage(null);
return;
}
final Schema schema = loader.createSchema(loaderParams);
// Run the algorithm.
final ArgumentUtils.TextProgress progress = new ArgumentUtils.TextProgress(pw);
Result result = algorithm.run(schema, algorithmParams, progress);
if (result == null) {
System.out.println("Algorithm was canceled.");
return;
}
// Process the results.
resultHandler.handle(resultHandlerParams, schema, result);
}
/**
* Converts spaces and punctuation to underscores.
*
* @param name Column identifier
* @return identifier with punctuation removed
*/
public static String depunctify(final String name) {
String s = name.replaceAll("[\\[\\]\\. _]+", "_");
s = s.replaceAll("^_", "");
s = s.replaceAll("_$", "");
return s;
}
}
// End Main.java