/*
* Copyright 2007 Zhang, Zheng <oldbig@gmail.com>
*
* This file is part of ZOJ.
*
* ZOJ 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 revision 3 of the License, or (at your option) any later revision.
*
* ZOJ 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 ZOJ. if not, see
* <http://www.gnu.org/licenses/>.
*/
package cn.edu.zju.acm.onlinejudge.util;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.CopyUtils;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import cn.edu.zju.acm.onlinejudge.bean.Limit;
import cn.edu.zju.acm.onlinejudge.bean.Problem;
public class ProblemManager {
public static final String PROBLEM_CSV_FILE = "problems.csv";
public static final String INPUT_FILE = "input";
public static final String OUTPUT_FILE = "output";
public static final String PROBLEM_TEXT_FILE = "text";
public static final String CHECKER_FILE = "checker";
public static final String CHECKER_SOURCE_FILE = "checker";
public static final String JUDGE_SOLUTION_FILE = "solution";
public static final String IMAGES_DIR = "images";
public static ProblemPackage importProblem(InputStream in, ActionMessages messages) {
Map<String, byte[]> files = new HashMap<String, byte[]>();
ZipInputStream zis = null;
try {
zis = new ZipInputStream(new BufferedInputStream(in));
// zis = new ZipInputStream(new BufferedInputStream(new FileInputStream("d:/100.zip")));
ZipEntry entry = zis.getNextEntry();
while (entry != null) {
if (!entry.isDirectory()) {
// TODO the file may be too big. > 100M
/*
* byte data[] = new byte[(int) entry.getSize()]; int l = 0; while (l < data.length) { int ll =
* zis.read(data, l, data.length - l); if (ll < 0) { break; } l += ll; }
*/
ByteArrayOutputStream buf = new ByteArrayOutputStream();
CopyUtils.copy(zis, buf);
files.put(entry.getName(), buf.toByteArray());
}
entry = zis.getNextEntry();
}
} catch (IOException ioe) {
messages.add("error", new ActionMessage("onlinejudge.importProblem.invalidzip"));
return null;
} finally {
try {
if (zis != null) {
zis.close();
}
} catch (IOException e) {
// ignore
}
}
/*
* files.clear(); files.put("problems.csv", "3,a\n2,b,true,1,2,3,4,a,b,c\n1,c".getBytes());
* files.put("1\\checker", "checker".getBytes()); files.put("1\\input", "input".getBytes());
* files.put("1\\output", "output".getBytes()); files.put("3\\checker", "checker3".getBytes());
* files.put("3\\input", "input3".getBytes()); files.put("3\\output", "output3".getBytes());
* files.put("images\\1.jpg", "1".getBytes()); files.put("images\\2\\2.jpg", "2".getBytes());
*/
if (!files.containsKey(ProblemManager.PROBLEM_CSV_FILE)) {
messages.add("error", new ActionMessage("onlinejudge.importProblem.missingproblemscsv"));
return null;
}
ProblemPackage problemPackage = ProblemManager.parse(files, messages);
if (messages.size() > 0) {
return null;
}
return problemPackage;
}
private static ProblemPackage parse(Map<String, byte[]> files, ActionMessages messages) {
Map<String, ProblemEntry> entryMap = new TreeMap<String, ProblemEntry>();
byte[] csv = files.get(ProblemManager.PROBLEM_CSV_FILE);
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(csv)));
int index = 0;
try {
for (;;) {
index++;
String messageKey = "Line " + index;
String line = reader.readLine();
if (line == null) {
break;
}
if (line.trim().length() == 0) {
continue;
}
// CSV format code,title,checker,tl,ml,ol,sl,author,source,contest
String[] values = ProblemManager.split(line);
Problem problem = new Problem();
if (values.length < 2) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidline"));
continue;
}
if (values[0].length() > 8 || values[0].length() == 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidcode"));
}
problem.setCode(values[0]);
if (values[1].length() > 128 || values[1].length() == 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidtitle"));
}
problem.setTitle(values[1]);
if (values.length > 2 && Boolean.valueOf(values[2]).booleanValue()) {
problem.setChecker(true);
}
Limit limit = new Limit();
Integer tl = ProblemManager.retrieveInt(values, 3);
if (tl != null) {
if (tl.intValue() < 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidtl"));
} else {
limit.setTimeLimit(tl.intValue());
}
}
Integer ml = ProblemManager.retrieveInt(values, 4);
if (ml != null) {
if (ml.intValue() < 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidml"));
} else {
limit.setMemoryLimit(ml.intValue());
}
}
Integer ol = ProblemManager.retrieveInt(values, 5);
if (ol != null) {
if (ol.intValue() < 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidol"));
} else {
limit.setOutputLimit(ol.intValue());
}
}
Integer sl = ProblemManager.retrieveInt(values, 6);
if (sl != null) {
if (sl.intValue() < 0) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidsl"));
} else {
limit.setSubmissionLimit(sl.intValue());
}
}
if (tl != null || ml != null || ol != null || sl != null) {
if (tl != null && ml != null && ol != null && sl != null) {
problem.setLimit(limit);
} else {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.missinglimit"));
}
}
if (values.length > 7 && values[7].length() > 0) {
if (values[7].length() > 32) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidauthor"));
} else {
problem.setAuthor(values[7]);
}
}
if (values.length > 8 && values[8].length() > 0) {
if (values[8].length() > 128) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidsource"));
} else {
problem.setSource(values[8]);
}
}
if (values.length > 9 && values[9].length() > 0) {
if (values[9].length() > 128) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.invalidcontest"));
} else {
problem.setContest(values[9]);
}
}
ProblemEntry entry = new ProblemEntry();
entry.setProblem(problem);
if (entryMap.containsKey(problem.getCode())) {
messages.add(messageKey, new ActionMessage("onlinejudge.importProblem.reduplicatecode"));
}
entryMap.put(problem.getCode(), entry);
}
} catch (IOException e) {
messages.add("error", new ActionMessage("onlinejudge.importProblem.invalidproblemscsv"));
}
if (messages.size() > 0) {
return null;
}
if (entryMap.size() == 0) {
messages.add("error", new ActionMessage("onlinejudge.importProblem.emptyproblemscsv"));
}
ProblemPackage problemPackage = new ProblemPackage();
problemPackage.setProblemEntries(new ProblemEntry[entryMap.size()]);
// retrieve checker, input, output
index = 0;
for (String string : entryMap.keySet()) {
ProblemEntry entry = entryMap.get(string);
String code = entry.getProblem().getCode();
byte[] checker = files.get(code + "/" + ProblemManager.CHECKER_FILE);
byte[] input = files.get(code + "/" + ProblemManager.INPUT_FILE);
byte[] output = files.get(code + "/" + ProblemManager.OUTPUT_FILE);
byte[] text = files.get(code + "/" + ProblemManager.PROBLEM_TEXT_FILE);
byte[] solution = null;
byte[] checkerSource = null;
String checkerSourceType = ProblemManager.getFileType(code, ProblemManager.CHECKER_SOURCE_FILE, files);
if (checkerSourceType != null) {
checkerSource = files.get(code + "/" + ProblemManager.CHECKER_SOURCE_FILE + "." + checkerSourceType);
}
String solutionType = ProblemManager.getFileType(code, ProblemManager.JUDGE_SOLUTION_FILE, files);
if (solutionType != null) {
solution = files.get(code + "/" + ProblemManager.JUDGE_SOLUTION_FILE + "." + solutionType);
}
if ("cpp".equals(checkerSourceType)) {
checkerSourceType = "cc";
}
if ("cpp".equals(solutionType)) {
solutionType = "cc";
}
entry.setChecker(checker);
entry.setInput(input);
entry.setOutput(output);
entry.setText(text);
entry.setSolution(solution);
entry.setSolutionType(solutionType);
entry.setCheckerSource(checkerSource);
entry.setCheckerSourceType(checkerSourceType);
problemPackage.getProblemEntries()[index] = entry;
index++;
}
// retrieve images
Map<String, byte[]> imageMap = new HashMap<String, byte[]>();
Map<String, String> usedImages = new HashMap<String, String>();
Map<String, String> duplicateImages = new HashMap<String, String>();
for (String string : files.keySet()) {
String path = string;
if (ProblemManager.isImageFile(path)) {
String imageName = new File(path).getName();
if (imageMap.containsKey(imageName)) {
String s = duplicateImages.get(imageName);
s = (s == null ? "" : s) + " " + path;
duplicateImages.put(imageName, s);
}
if (ProblemManager.isUsed(imageName)) {
String s = usedImages.get(imageName);
s = (s == null ? "" : s) + " " + path;
usedImages.put(imageName, s);
}
imageMap.put(imageName, files.get(path));
}
}
problemPackage.setImages(imageMap);
problemPackage.setUsedImages(usedImages);
problemPackage.setDuplicateImages(duplicateImages);
return problemPackage;
}
private static boolean isImageFile(String path) {
String name = path.toLowerCase();
return name.endsWith(".bmp") || path.endsWith(".gif") || path.endsWith(".tif") || path.endsWith(".png") ||
path.endsWith(".jpg") || path.endsWith(".jpeg");
}
private static boolean isUsed(String image) {
File f = new File(ConfigManager.getImagePath(), image);
return f.exists();
}
private static String getFileType(String code, String name, Map<String, byte[]> files) {
if (files.containsKey(code + "/" + name + ".cc")) {
return "cc";
} else if (files.containsKey(code + "/" + name + ".cpp")) {
return "cpp";
} else if (files.containsKey(code + "/" + name + ".c")) {
return "c";
} else if (files.containsKey(code + "/" + name + ".pas")) {
return "pas";
} else if (files.containsKey(code + "/" + name + ".fpc")) {
return "fpc";
} else if (files.containsKey(code + "/" + name + ".java")) {
return "java";
} else {
return null;
}
}
private static Integer retrieveInt(String[] values, int index) {
if (values.length > index && values[index].length() > 0) {
try {
return Integer.valueOf(values[index]);
} catch (Exception e) {
return new Integer(-1);
}
}
return null;
}
private static String[] split(String line) {
int quote = 0;
StringBuffer sb = new StringBuffer();
List<String> values = new ArrayList<String>();
for (int i = 0; i < line.length(); ++i) {
char ch = line.charAt(i);
if (ch == '"') {
if (quote == 0 && sb.length() == 0) {
quote = 1;
continue;
}
if (quote == 0) {
sb.append(ch);
continue;
}
if (quote == 2) {
sb.append(ch);
quote--;
} else {
quote++;
}
} else if (quote == 1) {
sb.append(ch);
} else if (ch == ',') {
values.add(sb.toString().trim());
sb = new StringBuffer();
quote = 0;
} else {
sb.append(ch);
}
}
values.add(sb.toString().trim());
return values.toArray(new String[0]);
}
}