package org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.help; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import java.net.URISyntaxException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Map; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.NoSuchRascalFunction; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.ideservices.BasicIDEServices; import org.rascalmpl.library.experiments.Compiler.RVM.Interpreter.repl.CommandExecutor; import org.rascalmpl.library.experiments.tutor3.Feedback; import org.rascalmpl.library.util.PathConfig; import org.rascalmpl.parser.gtd.exception.ParseError; import org.rascalmpl.uri.URIResolverRegistry; import org.rascalmpl.uri.URIUtil; import org.rascalmpl.value.IConstructor; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.IList; import org.rascalmpl.value.ISourceLocation; import org.rascalmpl.value.IString; import org.rascalmpl.value.ITuple; import org.rascalmpl.value.IValue; import org.rascalmpl.value.IValueFactory; import org.rascalmpl.values.ValueFactoryFactory; import fi.iki.elonen.NanoHTTPD; import fi.iki.elonen.NanoHTTPD.Response.Status; public class HelpServer extends NanoHTTPD { private final ISourceLocation root; private final HelpManager helpManager; private CommandExecutor executor; IValueFactory vf = ValueFactoryFactory.getValueFactory(); StringWriter outWriter; PrintWriter outPrintWriter; StringWriter errWriter; PrintWriter errPrintWriter; public HelpServer(int port, HelpManager helpManager, ISourceLocation root) throws IOException { super(port); start(); this.helpManager = helpManager; this.root = root; } @Override public Response serve(String uri, Method method, Map<String, String> headers, Map<String, String> parms, Map<String, String> files) { Response response; System.err.println("serve: " + uri); if(uri.startsWith("/Search")){ try { String[] words = ("help " + URLDecoder.decode(parms.get("searchFor"), "UTF-8")).split(" "); return newFixedLengthResponse(Status.OK, "text/html", helpManager.giveHelp(words)); } catch (UnsupportedEncodingException e) { return newFixedLengthResponse(Status.OK, "text/plain", e.getStackTrace().toString()); } } if(uri.startsWith("/ValidateCodeQuestion")){ try { if(parms.get("listing") == null || parms.get("question") == null || parms.get("hole1") == null){ newFixedLengthResponse(Status.NOT_FOUND, "text/plain", "missing listing, question or hole1 parameter"); } String listing = URLDecoder.decode(parms.get("listing"), "UTF-8"); String question = URLDecoder.decode(parms.get("question"), "UTF-8"); ArrayList<String> holes = new ArrayList<>(); for(int i = 1; parms.containsKey("hole" + i); i++){ holes.add(URLDecoder.decode(parms.get("hole" + i), "UTF-8")); } int k = 0; while(listing.indexOf("_") >= 0 && k < holes.size()){ listing = listing.replaceFirst("_", holes.get(k++)); } if(executor == null){ PathConfig pcfg = helpManager.getPathConfig(); outWriter = new StringWriter(); outPrintWriter = new PrintWriter(outWriter); errWriter = new StringWriter(); errPrintWriter = new PrintWriter(errWriter); pcfg = pcfg.addSourceLoc(vf.sourceLocation("test-modules", "", "")); executor = new CommandExecutor(pcfg, outPrintWriter, errPrintWriter, new BasicIDEServices(), null); } else { outWriter.getBuffer().setLength(0); errWriter.getBuffer().setLength(0); } writeModule(question, listing); try { IConstructor tr = executor.executeTestsRaw(question); System.err.println(tr); outPrintWriter.flush(); errPrintWriter.flush(); return newFixedLengthResponse(Status.OK, "application/json", formatTestResults(tr)); } catch (ParseError e){ return newFixedLengthResponse(Status.OK, "application/json", "{ \"ok\": false, \"failed\": [], \"exceptions\": [], \"syntax\": " + makeLoc(e) + " }"); } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchRascalFunction e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (URISyntaxException e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { ISourceLocation requestedItem = URIUtil.correctLocation(root.getScheme(), root.getAuthority(), root.getPath() + "/" + normalize(uri)); response = newChunkedResponse(Status.OK, getMimeType(uri), URIResolverRegistry.getInstance().getInputStream(requestedItem)); addHeaders(response, uri, headers); return response; } catch (IOException e) { return newFixedLengthResponse(Status.NOT_FOUND, "text/plain", uri + " not found.\n" + e); } } String makeLoc(ParseError e){ return "{" + "\"beginLine\": " + e.getBeginLine() + ", " + "\"beginColumn\": " + e.getBeginColumn() + ", " + "\"endLine\": " + e.getEndLine() + ", " + "\"endColumn\": " + e.getEndColumn() + "}"; } String makeLoc(ISourceLocation l){ return "{" + "\"beginLine\": " + l.getBeginLine() + ", " + "\"beginColumn\": " + l.getBeginColumn() + ", " + "\"endLine\": " + l.getEndLine() + ", " + "\"endColumn\": " + l.getEndColumn() + "}"; } String makeResult(ISourceLocation l, IString msg){ return "{" + "\"src\": " + makeLoc(l) + ", " + "\"msg\": " + msg + "}"; } String formatTestResults(IConstructor tr){ IList results = (IList) tr.get("results"); IList exceptions = (IList) tr.get("exceptions"); boolean ok = true; String failed = "["; IInteger zero = vf.integer(0); String sep = ""; for(IValue v : results){ ITuple tup = (ITuple) v; if(tup.get(1).equals(zero)){ ok = false; failed += sep + makeResult((ISourceLocation) tup.get(0), (IString) tup.get(2)) ; sep = ", "; } } failed += "]"; String sexceptions = "["; sep = ""; for(IValue v : exceptions){ sexceptions += sep + ((IString) v).getValue(); sep = ", "; } sexceptions += "]"; ok &= exceptions.length() == 0; return "{" + "\"ok\": " + ok + ", " + "\"failed\": " + failed + "," + "\"exceptions\": " + sexceptions + "," + "\"feedback\": " + Feedback.give(ok) + "}"; } private void writeModule(String question, String listing) throws IOException, URISyntaxException { URIResolverRegistry reg = URIResolverRegistry.getInstance(); ISourceLocation sloc = vf.sourceLocation("test-modules", "", question + ".rsc"); OutputStream out = reg.getOutputStream(sloc, false); listing = listing.replaceAll("\\\\n", "\n"); out.write(listing.getBytes(), 0, listing.length()); out.close(); System.err.println("written to " + sloc + ":" + "\n" + listing); } String getExtension(String uri){ int n = uri.lastIndexOf("."); if(n >= 0){ return uri.substring(n + 1); } return ""; } String getMimeType(String uri){ switch(getExtension(uri)){ case "css": return "text/css"; case "ico": return "image/x-icon"; case "html": return "text/html"; case "jpeg": return "image/jpeg"; case "png": return "image/png"; case "txt": return "text/plain"; } return "text/html"; } private String etag(String uri){ String parts[] = uri.split("#"); return String.valueOf(parts[0].hashCode()); } private String normalize(String uri){ if(uri.startsWith("/")){ uri = uri.substring(1, uri.length()); } String[] parts = uri.split("#"); if(parts.length >= 2){ return parts[0] + "/index.html#" + parts[1]; } if(!uri.contains(".")){ return uri + "/index.html"; } return uri; } private void addHeaders(Response response, String uri, Map<String, String> headers) { response.addHeader("Cache-Control", "max-age=8600, public"); response.addHeader("ETag", etag(uri)); for (String key : headers.keySet()) { response.addHeader(key, headers.get(key)); } } }