package org.psjava.judgesubmit;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.TreeSet;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.psjava.ds.array.Array;
import org.psjava.ds.array.DynamicArray;
import org.psjava.ds.map.KeysInMap;
import org.psjava.ds.map.Map;
import org.psjava.ds.numbersystrem.LongNumberSystem;
import org.psjava.ds.set.Set;
import org.psjava.ds.set.SetFromVarargs;
import org.psjava.formula.CeilingDivide;
import org.psjava.util.Pair;
import org.psjava.util.StringMerger;
public class Console {
// TODO remove unknown after migration
public static final Set<SubmitStatusCode> ON_PROGRESS = SetFromVarargs.create(SubmitStatusCode.COMPILING, SubmitStatusCode.RUNNING, SubmitStatusCode.WAITING, SubmitStatusCode.UNKNOWN, SubmitStatusCode.JUDGING);
public static void main(String[] args) {
if (args.length == 0) {
printUsage();
return;
}
CommandLineParser parser = new GnuParser();
try {
CommandLine cmd = parser.parse(createOptions(), args);
String[] withoutOptionArgs = cmd.getArgs();
if (withoutOptionArgs.length < 3)
throw new InvalidCommandRuleException("too few parameters");
if (withoutOptionArgs.length > 3)
throw new InvalidCommandRuleException("too many parameters");
String siteCode = withoutOptionArgs[0];
String problemId = withoutOptionArgs[1];
String inputCharacterSet = cmd.getOptionValue("c", "UTF-8");
File sourceFile = new File(withoutOptionArgs[2]);
if (!JudgeSiteMap.get().containsKey(siteCode))
throw new InvalidCommandRuleException("Unknown site code: " + siteCode);
if (!sourceFile.exists())
throw new InvalidInputException("Source file is not exist: " + sourceFile.getAbsolutePath());
if (!Charset.isSupported(inputCharacterSet))
throw new InvalidCommandRuleException("Unsupported character set: " + inputCharacterSet);
java.util.Set<String> searchDirs = new TreeSet<String>();
Language language;
if (cmd.hasOption("l")) {
String code = cmd.getOptionValue("l");
language = toLanguage(code, null);
if (language == null)
throw new InvalidCommandRuleException("Invalid language code: " + code);
} else {
if (!LanguageDetector.isSupported(sourceFile))
throw new InvalidCommandRuleException("Unsupported file type: " + sourceFile.getName());
language = LanguageDetector.detect(sourceFile);
if (cmd.hasOption("s")) {
if (!CodeCombineAdapter.isSupported(language))
throw new InvalidCommandRuleException("Not supported language for CodeCombine: " + language);
for (String dir : cmd.getOptionValues("s")) {
if (!new File(dir).exists())
throw new InvalidInputException("Search directory is not exist: " + dir);
searchDirs.add(dir);
}
}
}
String userid;
String password;
if (cmd.hasOption('u'))
userid = cmd.getOptionValue('u', "");
else
userid = Util.readNormal("Enter user id: ");
if (cmd.hasOption('p'))
password = cmd.getOptionValue('p', "");
else
password = Util.readPassword("Enter password: ");
JudgeSite site = JudgeSiteMap.get().get(siteCode);
FileInputStream fis = new FileInputStream(sourceFile);
String code = StreamUtil.readAllAsString(fis, inputCharacterSet);
File wordFileOrNull = null;
if(cmd.hasOption('r'))
wordFileOrNull = new File(cmd.getOptionValue('r'));
JudgeSubmit.submitAngGetResult(site, problemId, userid, password, code, language, searchDirs, new SubmitMonitorListener() {
@Override
public void statusChanged(SubmitStatus judgeStatus) {
System.out.println(getTimeDescription(judgeStatus.getTimeUsage(-1)) + " " + getMemoryDescription(judgeStatus.getMemoryUsage(-1)) + " " + judgeStatus.getCode());
}
@Override
public void onCompileError(String message) {
System.out.println("Compile Error:");
System.out.println(message);
}
}, cmd.hasOption('k'), wordFileOrNull);
fis.close();
} catch (ParseException e) {
System.out.println("Invalid parameter: " + e.getMessage());
System.out.println();
printUsage();
} catch (InvalidCommandRuleException e) {
System.out.println("Invalid command line: " + e.getMessage());
System.out.println();
printUsage();
} catch (InvalidInputException e) {
System.out.println("Invalid Input: " + e.getMessage());
} catch (IOException e) {
System.out.println("I/O exception: " + e.toString());
} catch (SiteParsingException e) {
System.out.println("Parsing error:");
e.printStackTrace(); // TODO add crash report
} catch (JudgeServiceException e) {
System.out.println("Service error:");
e.printStackTrace(); // TODO add crash report
}
}
private static Options createOptions() {
Options options = new Options();
options.addOption("u", "user-id", true, "user id for the judge site");
options.addOption("k", "shrink", false, "shrink source code");
options.addOption("r", "replace", true, "replace words. word set is read from given text file (experimental)");
options.addOption("p", "password", true, "password for the judge site");
options.addOption("c", "char-set", true, "character set of input files. default is UTF-8");
options.addOption("l", "language", true, "language code of source file. without this option, language is detected by file's extension.");
DynamicArray<String> supported = DynamicArray.create();
for (Language lang : Language.values())
if (CodeCombineAdapter.isSupported(lang))
supported.addToLast(toLanguageCode(lang));
options.addOption("s", "search-path", true, "search path(s) for external source code\nsupported languages: " + StringMerger.merge(supported, ",") + "\n" + "merging is performed by CodeCombine\n(https://github.com/abbajoa/codecombine)\n");
return options;
}
protected static Language toLanguage(String codeFromConsole, Language def) {
for (Pair<String, Language> p : getLanguageAndCodes())
if (p.v1.equals(codeFromConsole))
return p.v2;
return def;
}
protected static String toLanguageCode(Language lang) {
for (Pair<String, Language> p : getLanguageAndCodes())
if (p.v2.equals(lang))
return p.v1;
throw new RuntimeException();
}
private static Array<Pair<String, Language>> getLanguageAndCodes() {
DynamicArray<Pair<String, Language>> pairs = DynamicArray.create();
pairs.addToLast(Pair.create("java", Language.JAVA));
pairs.addToLast(Pair.create("cpp", Language.CPP));
pairs.addToLast(Pair.create("c", Language.C));
return pairs;
}
public static void printUsage() {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("judgesubmit [-options] [site code] [problem id] [source file path]", createOptions());
System.out.println();
System.out.println("supported judge site code:");
Map<String, JudgeSite> map = JudgeSiteMap.get();
for (String code : KeysInMap.get(map)) {
JudgeSite site = map.get(code);
System.out.println("\t" + code + "\t" + site.getName() + " (" + site.getSocketConnectionData().host + ")");
DynamicArray<String> supported = DynamicArray.create();
for (Language lang : site.getSupportedLanguageSet())
supported.addToLast(toLanguageCode(lang));
System.out.print("\t\tlanguage code: " + StringMerger.merge(supported, ","));
System.out.println();
}
}
public static String getTimeDescription(long timeOrNegativeOne) {
if (timeOrNegativeOne == -1) {
return "--:--.---s";
} else {
long remain = timeOrNegativeOne;
long milli = remain % 1000;
timeOrNegativeOne /= 1000;
long sec = timeOrNegativeOne % 60;
timeOrNegativeOne /= 60;
long min = timeOrNegativeOne;
return String.format("%02d:%02d.%03ds", min, sec, milli);
}
}
public static String getMemoryDescription(long usageOrNegativeOne) {
if (usageOrNegativeOne == -1) {
return "----MB";
} else {
long mb = CeilingDivide.calc(LongNumberSystem.getInstance(), usageOrNegativeOne, 1024L * 1024L);
return Util.constructRepeatedString(" ", 4 - Long.toString(mb).length()) + mb + "MB";
}
}
}