/*
This file is part of mjprof.
mjprof is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
mjprof 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.
You should have received a copy of the GNU General Public License
along with mjprof. If not, see <http://www.gnu.org/licenses/>.
*/
package com.performizeit.mjprof;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.performizeit.mjprof.api.Attr;
import com.performizeit.mjprof.api.ThreadInfoComparator;
import com.performizeit.mjprof.monads.MJStep;
import com.performizeit.mjprof.api.Param;
import com.performizeit.mjprof.monads.Macros;
import com.performizeit.mjprof.monads.StepInfo;
import com.performizeit.mjprof.monads.StepsRepository;
import com.performizeit.mjprof.parser.ThreadInfo;
import com.performizeit.mjprof.plugin.PluginUtils;
import com.performizeit.mjprof.plugin.types.*;
import com.performizeit.plumbing.Generator;
import com.performizeit.plumbing.GeneratorHandler;
import com.performizeit.plumbing.Pipe;
import com.performizeit.plumbing.PipeHandler;
public class MJProf {
public static void main(String[] args) throws IOException {
// System.setProperty("java.awt.headless","true"); // when using lanterna we suffer when there we do not disable GUI
if (args.length < 1) {
printSynopsisAndExit();
}
ArrayList<MJStep> steps = parseCommandLine(join(args, " ").trim());
if (steps == null) {
printSynopsisAndExit();
}
boolean foundExplicitDataSource = false;
for (MJStep mjstep : steps) {
Class clz = getClassFromStep(mjstep);
if (PluginUtils.isDataSourceClass(clz)) {
foundExplicitDataSource = true;
}
}
if (!foundExplicitDataSource) {
steps.add(0,new MJStep("stdin"));
}
MJStep lastStep = steps.get(steps.size()-1);
Class lststp = getClassFromStep(lastStep);
if (!PluginUtils.isOutputerClass(lststp)) {
steps.add(new MJStep("stdout"));
}
constructPlumbing(steps);
for (Pipe pipe : pipes) {
pipe.start();
}
for (Generator g : generators) {
g.start();
}
}
static ArrayList <Pipe> pipes = new ArrayList<Pipe>();
static ArrayList <Generator<ThreadInfo>> generators = new ArrayList<Generator<ThreadInfo>>();
private static void constructPlumbing(ArrayList<MJStep> steps) {
int i=0;
for (MJStep step:steps) { // create handlers
Pipe p;
Object stepObject = getObjectFromStep(step);
if (PluginUtils.isDataSource(stepObject)) {
MJStep noopStep = new MJStep("noop");
PipeHandler handler = (PipeHandler)getObjectFromStep(noopStep);
p = new Pipe("Pipe Thread "+step.getStepName() +i,handler);
GeneratorHandler genHandler = (GeneratorHandler)stepObject;
Generator<ThreadInfo> g = new Generator<ThreadInfo>("Generator Thread "+step.getStepName() +i,genHandler,p);
generators.add(g);
} else {
PipeHandler<ThreadInfo,ThreadInfo> handler = (PipeHandler<ThreadInfo,ThreadInfo>)stepObject;
p = new Pipe<ThreadInfo,ThreadInfo> ("Pipe Thread "+step.getStepName() +i,handler);
}
pipes.add(p);
if (i > 0) { //connect to previous pipe
Pipe prevPipe = pipes.get(i-1);
prevPipe.setOutgoingPipe(p);
}
i++;
}
}
private static Object getObjectFromStep(MJStep mjstep) {
StepInfo step = StepsRepository.getStep(mjstep.getStepName());
Object[] paramArgs = buildArgsArray(step.getParams(), mjstep.getStepArgs());
Object obj = PluginUtils.initObj(step.getClazz(), step.getParamTypes(), paramArgs);
return obj;
}
private static Class getClassFromStep(MJStep mjstep) {
StepInfo step = StepsRepository.getStep(mjstep.getStepName());
return step.getClazz();
}
private static void printSynopsisAndExit() {
System.err.println(getSynopsisString());
System.exit(1);
}
public static String getSynopsisString() {
StringBuilder sb = new StringBuilder();
sb.append("Synopsis\nA list of the following monads concatenated with . \n");
sb.append("\nExpanded Expression:\n");
sb.append(expandedExpressionstr+"\n");
List<String> keys = new ArrayList<String>(StepsRepository.getRepository().keySet());
Collections.sort(keys,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
if (o1.startsWith("-")) o1 = o1.substring(1)+"-";
if (o2.startsWith("-")) o2 = o2.substring(1)+"-";
return o1.compareTo(o2);
}
});
sb.append("\nData sources:\n");
getSynopsisContent(sb, keys, DataSource.class);
sb.append("\nOutput:\n");
getSynopsisContent(sb, keys,Outputer.class);
sb.append("\nFilters:\n");
getSynopsisContent(sb, keys, Filter.class);
sb.append("\nSingle thread mappers:\n");
getSynopsisContent(sb, keys, SingleThreadMapper.class);
sb.append("\nFull dump mappers:\n");
getSynopsisContent(sb, keys,DumpReducer.class);
getSynopsisContent(sb, keys,ThreadInfoComparator.class);
sb.append("\nTerminals:\n");
getSynopsisContent(sb, keys,Terminal.class);
sb.append("\n help -Prints this message");
sb.append("\n\nMacros:\n");
Macros.getInstance().getSynopsisContent(sb);
return sb.toString();
}
private static void getSynopsisContent(StringBuilder sb, List<String> keys,Class pluginType) {
int lineLength = 0;
StepInfo stepInfo;
for (String stepName : keys) {
lineLength = 0;
stepInfo = StepsRepository.getStep(stepName);
if (!stepInfo.getPluginType().equals(pluginType)) continue;
lineLength += appendAndCount(sb, " " + stepName);
if (stepInfo.getArgNum() > 0) {
lineLength += appendAndCount(sb, "/");
Param[] params = stepInfo.getParams();
for (int i = 0; i < stepInfo.getArgNum(); i++) {
String paramName = params[i].value();
if (paramName == null || paramName.length() == 0) // the default paramter name is ""
paramName = params[i].type().getSimpleName().toLowerCase();
if (params[i].optional()) {
paramName = "["+paramName+"]";
}
lineLength += appendAndCount(sb,paramName );
if (i < stepInfo.getArgNum() - 1) {
lineLength += appendAndCount(sb, ",");
}
}
lineLength += appendAndCount(sb, "/");
}
for (int j = 0; j < (70 - lineLength); j++) {
sb.append(" ");
}
sb.append("- ");
sb.append(stepInfo.getDescription());
sb.append("\n");
}
}
private static int appendAndCount(StringBuilder command, String str) {
command.append(str);
return str.length();
}
// a separator between steps can be either a period of a space if part of argument list (inside // it is ignored)
static int findNextSeperator(String str) {
boolean insideArgList = false;
for (int i = 0; i < str.length(); i++) {
if (!insideArgList ) {
if (str.charAt(i) == '/') insideArgList = true; // / starts arg list
} else {
if (str.charAt(i) == '/') { // / will end arg list
if (i<str.length()-1 && insideArgList && str.charAt(i+1) !='.' && str.charAt(i+1) !=' ') {
// but we have to have a space or . afterwards otherwise it is part of the arguments
} else insideArgList = false;
}
}
if ((str.charAt(i) == '.' || str.charAt(i) == ' ') && !insideArgList) return i;
}
return -1;
}
static ArrayList<String> splitCommandLine(String arg) {
String argPart = arg;
ArrayList<String> argParts = new ArrayList<String>();
for (int idx = findNextSeperator(argPart); idx != -1; idx = findNextSeperator(argPart)) {
argParts.add(argPart.substring(0, idx));
argPart = argPart.substring(idx + 1);
}
argParts.add(argPart);
return argParts;
}
static String expandedExpressionstr="";
static ArrayList<MJStep> parseCommandLine(String concatArgs) {
ArrayList<String> argParts = splitCommandLine(concatArgs);
ArrayList<String> finalArgsParts = new ArrayList<String>();
for (String argPart : argParts) {
String expand = Macros.getInstance().getProperty(argPart);
if (expand != null) {
ArrayList<String> expandParts = splitCommandLine(expand);
finalArgsParts.addAll(expandParts);
} else {
finalArgsParts.add(argPart);
}
}
argParts = finalArgsParts;
StringBuilder expandedExpression = new StringBuilder();
for(int i = 0;i<argParts.size();i++ ) {
expandedExpression.append(argParts.get(i));
if (i<argParts.size()-1) expandedExpression .append(".");
}
expandedExpressionstr = expandedExpression.toString();
ArrayList<MJStep> mjsteps = new ArrayList<MJStep>();
for (String s : argParts) {
if (s.equalsIgnoreCase("help")) {
printSynopsisAndExit();
}
MJStep step = new MJStep(s);
if (!StepsRepository.stepValid(step)) {
System.err.println("Step " + step + " is invalid\n");
return null;
}
mjsteps.add(step);
}
return mjsteps;
}
public static String join(String[] strs, String delim) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < strs.length; i++) {
b.append(strs[i]);
if (i < strs.length - 1) b.append(delim);
}
return b.toString();
}
public static Object[] buildArgsArray(Param[] params, List<String> paramsVals) {
Object[] paramsTrans = new Object[params.length];
for (int i = 0; i < params.length; i++) {
try {
String val;
if (paramsVals.size() <= i ) {
if (params[i].optional()) {
val = params[i].defaultValue();
} else {
throw new RuntimeException("Non optional parameter" + params[i].value() + " is missing ");
// TODO improve error handling
}
} else {
val = paramsVals.get(i);
}
if (params[i].type().equals(Integer.class) || params[i].type().equals(int.class)) {
paramsTrans[i] = Integer.parseInt(val);
} else if (params[i].type().equals(Long.class) || params[i].type().equals(long.class)) {
paramsTrans[i] = Long.parseLong(val);
} else if (params[i].type().equals(Boolean.class) || params[i].type().equals(boolean.class)) {
paramsTrans[i] = Boolean.parseBoolean(val);
} else if (params[i].type().equals(Attr.class) ) {
paramsTrans[i] = new Attr(val);
} else {
paramsTrans[i] = val;
}
} catch (NumberFormatException e) {
System.err.println("Please re-enter - wrong parameter format.");
}
}
return paramsTrans;
}
}