From 02bb6cc073efc44a8a25be352510eee3e6aa7cb4 Mon Sep 17 00:00:00 2001 From: krasimir Date: Sat, 24 Oct 2009 10:38:21 +0000 Subject: [PATCH] added tree visualizations in TranslateApp --- src/server/FastCGIUtils.hs | 8 +- src/server/PGFService.hs | 51 +++++-- .../chalmers/cs/gf/gwt/client/FridgeApp.java | 4 +- .../cs/gf/gwt/client/JSONRequestBuilder.java | 6 +- .../src/se/chalmers/cs/gf/gwt/client/PGF.java | 35 ++++- .../chalmers/cs/gf/gwt/client/PGFWrapper.java | 42 +++++- .../cs/gf/gwt/client/TranslateApp.java | 126 +++++++++++++++++- .../chalmers/cs/gf/gwt/public/Translate.css | 31 ++++- .../chalmers/cs/gf/gwt/public/align-btn.png | Bin 0 -> 138 bytes .../se/chalmers/cs/gf/gwt/public/tree-btn.png | Bin 0 -> 149 bytes 10 files changed, 271 insertions(+), 32 deletions(-) create mode 100644 src/server/gwt/src/se/chalmers/cs/gf/gwt/public/align-btn.png create mode 100644 src/server/gwt/src/se/chalmers/cs/gf/gwt/public/tree-btn.png diff --git a/src/server/FastCGIUtils.hs b/src/server/FastCGIUtils.hs index 4c720e216..603ca0db3 100644 --- a/src/server/FastCGIUtils.hs +++ b/src/server/FastCGIUtils.hs @@ -2,7 +2,8 @@ module FastCGIUtils (initFastCGI, loopFastCGI, throwCGIError, handleCGIErrors, stderrToFile, - outputJSONP, + outputJSONP, + outputPNG, splitBy) where import Control.Concurrent @@ -164,6 +165,11 @@ outputJSONP x = setHeader "Content-Type" "text/json; charset=utf-8" outputStrict $ UTF8.encodeString str +outputPNG :: String -> CGI CGIResult +outputPNG x = do + setHeader "Content-Type" "image/png" + outputStrict x + outputStrict :: String -> CGI CGIResult outputStrict x | x == x = output x | otherwise = fail "I am the pope." diff --git a/src/server/PGFService.hs b/src/server/PGFService.hs index d0fac03d9..c4290b7da 100644 --- a/src/server/PGFService.hs +++ b/src/server/PGFService.hs @@ -19,6 +19,7 @@ import qualified Data.Map as Map import Data.Maybe import System.Directory import System.FilePath +import System.Process logFile :: FilePath logFile = "pgf-error.log" @@ -37,18 +38,20 @@ cgiMain cache = do path <- getVarWithDefault "SCRIPT_FILENAME" "" pgf <- liftIO $ readCache cache path command <- liftM (maybe "grammar" (urlDecodeUnicode . UTF8.decodeString)) (getInput "command") - jsonp <- pgfMain pgf command - outputJSONP jsonp + pgfMain pgf command -pgfMain :: PGF -> String -> CGI JSValue +pgfMain :: PGF -> String -> CGI CGIResult pgfMain pgf command = case command of - "parse" -> return (doParse pgf) `ap` getText `ap` getCat `ap` getFrom - "complete" -> return (doComplete pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getLimit - "linearize" -> return (doLinearize pgf) `ap` getTree `ap` getTo - "random" -> getCat >>= \c -> getLimit >>= liftIO . doRandom pgf c - "translate" -> return (doTranslate pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getTo - "grammar" -> return (doGrammar pgf) `ap` requestAcceptLanguage + "parse" -> return (doParse pgf) `ap` getText `ap` getCat `ap` getFrom >>= outputJSONP + "complete" -> return (doComplete pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getLimit >>= outputJSONP + "linearize" -> return (doLinearize pgf) `ap` getTree `ap` getTo >>= outputJSONP + "random" -> getCat >>= \c -> getLimit >>= liftIO . doRandom pgf c >>= outputJSONP + "translate" -> return (doTranslate pgf) `ap` getText `ap` getCat `ap` getFrom `ap` getTo >>= outputJSONP + "grammar" -> return (doGrammar pgf) `ap` requestAcceptLanguage >>= outputJSONP + "abstrtree" -> getTree >>= liftIO . doGraphvizAbstrTree pgf >>= outputPNG + "parsetree" -> getTree >>= \t -> getFrom >>= \(Just l) -> liftIO (doGraphvizParseTree pgf l t) >>= outputPNG + "alignment" -> getTree >>= liftIO . doGraphvizAlignment pgf >>= outputPNG _ -> throwCGIError 400 "Unknown command" ["Unknown command: " ++ show command] where getText :: CGI String @@ -68,7 +71,7 @@ pgfMain pgf command = Just cat -> case PGF.readType cat of Nothing -> throwCGIError 400 "Bad category" ["Bad category: " ++ cat] Just typ | typ `elem` PGF.categories pgf -> return $ Just typ - | otherwise -> throwCGIError 400 "Unknown category" ["Unknown category: " ++ show typ] + | otherwise -> throwCGIError 400 "Unknown category" ["Unknown category: " ++ PGF.showType [] typ] getFrom :: CGI (Maybe PGF.Language) getFrom = getLang "from" @@ -98,11 +101,15 @@ doListGrammars = return $ showJSON $ map toJSObject [[("name", f)] | f <- fs] doTranslate :: PGF -> String -> Maybe PGF.Type -> Maybe PGF.Language -> Maybe PGF.Language -> JSValue -doTranslate pgf input mcat mfrom mto = showJSON $ map toJSObject - [[("from", PGF.showLanguage from),("to", PGF.showLanguage to),("text",output)] +doTranslate pgf input mcat mfrom mto = + showJSON + [toJSObject [("from", showJSON (PGF.showLanguage from)), + ("tree", showJSON tree), + ("linearizations",showJSON [toJSObject [("to", PGF.showLanguage to),("text",output)] + | (to,output) <- linearize' pgf mto tree]) + ] | (from,trees) <- parse' pgf input mcat mfrom, - tree <- trees, - (to,output) <- linearize' pgf mto tree] + tree <- trees] doParse :: PGF -> String -> Maybe PGF.Type -> Maybe PGF.Language -> JSValue doParse pgf input mcat mfrom = showJSON $ map toJSObject @@ -141,10 +148,26 @@ doGrammar pgf macc = showJSON $ toJSObject | l <- PGF.languages pgf] categories = map toJSObject [[("cat", PGF.showType [] cat)] | cat <- PGF.categories pgf] +doGraphvizAbstrTree pgf tree = do + let dot = PGF.graphvizAbstractTree pgf (True,True) tree + readProcess "dot" ["-T","png"] dot + +doGraphvizParseTree pgf lang tree = do + let dot = PGF.graphvizParseTree pgf lang tree + readProcess "dot" ["-T","png"] (UTF8.encodeString dot) + +doGraphvizAlignment pgf tree = do + let dot = PGF.graphvizAlignment pgf tree + readProcess "dot" ["-T","png"] (UTF8.encodeString dot) + instance JSON PGF.CId where readJSON x = readJSON x >>= maybe (fail "Bad language.") return . PGF.readLanguage showJSON = showJSON . PGF.showLanguage +instance JSON PGF.Expr where + readJSON x = readJSON x >>= maybe (fail "Bad expression.") return . PGF.readExpr + showJSON = showJSON . PGF.showExpr [] + -- * PGF utilities cat :: PGF -> Maybe PGF.Type -> PGF.Type diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/FridgeApp.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/FridgeApp.java index 6938fae49..c0bfc376e 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/FridgeApp.java +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/FridgeApp.java @@ -58,7 +58,9 @@ public class FridgeApp implements EntryPoint { public void onResult (PGF.Translations translations) { outputPanel.removeStyleDependentName("working"); for (PGF.Translation t : translations.iterable()) { - outputPanel.add(createTranslation(t.getTo(), t.getText())); + for (PGF.Linearization l : t.getLinearizations().iterable()) { + outputPanel.add(createTranslation(l.getTo(), l.getText())); + } } } public void onError (Throwable e) { diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/JSONRequestBuilder.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/JSONRequestBuilder.java index 5453bf8a3..6ddd74ee8 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/JSONRequestBuilder.java +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/JSONRequestBuilder.java @@ -25,7 +25,7 @@ public class JSONRequestBuilder { } public static JSONRequest sendRequest (String base, List vars, final JSONCallback callback) { - String url = base + "?" + buildQueryString(vars); + String url = getQueryURL(base,vars); RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, url); builder.setTimeoutMillis(30000); builder.setHeader("Accept","text/plain, text/html;q=0.5, */*;q=0.1"); @@ -57,8 +57,10 @@ public class JSONRequestBuilder { return eval('(' + json + ')'); }-*/; - private static String buildQueryString(List args) { + public static String getQueryURL(String base, List args) { StringBuffer sb = new StringBuffer(); + sb.append(base); + sb.append("?"); if (args != null) { for (Arg arg : args) { if (arg.value != null) { diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java index 9d62c2ea7..80af107bb 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGF.java @@ -60,8 +60,19 @@ public class PGF { protected Translation() { } public final native String getFrom() /*-{ return this.from; }-*/; + public final native String getTree() /*-{ return this.tree; }-*/; + public final native Linearizations getLinearizations() /*-{ return this.linearizations; }-*/; + } + + public static class Linearizations extends IterableJsArray { + protected Linearizations() { } + } + + public static class Linearization extends JavaScriptObject { + protected Linearization() { } + public final native String getTo() /*-{ return this.to; }-*/; - public final native String getText() /*-{ return this.text; }-*/; + public final native String getText() /*-{ return this.text; }-*/; } /* Completion */ @@ -118,6 +129,28 @@ public class PGF { public final native String getTree() /*-{ return this.tree; }-*/; } + public String graphvizAbstractTree(String pgfURL, String abstractTree) { + List args = new ArrayList(); + args.add(new Arg("command", "abstrtree")); + args.add(new Arg("tree", abstractTree)); + return JSONRequestBuilder.getQueryURL(pgfURL,args); + } + + public String graphvizParseTree(String pgfURL, String abstractTree, String lang) { + List args = new ArrayList(); + args.add(new Arg("command", "parsetree")); + args.add(new Arg("tree", abstractTree)); + args.add(new Arg("from", lang)); + return JSONRequestBuilder.getQueryURL(pgfURL,args); + } + + public String graphvizAlignment(String pgfURL, String abstractTree) { + List args = new ArrayList(); + args.add(new Arg("command", "alignment")); + args.add(new Arg("tree", abstractTree)); + return JSONRequestBuilder.getQueryURL(pgfURL,args); + } + /* Common */ public JSONRequest sendGrammarRequest(String pgfURL, String resource, List args, final JSONCallback callback) { diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFWrapper.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFWrapper.java index 40d766c10..329ae5f32 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFWrapper.java +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/PGFWrapper.java @@ -12,10 +12,12 @@ public class PGFWrapper { private String baseURL; - private PGF pgf; + private String grammarURL; private String pgfName = null; + private PGF pgf; + private String inputLanguage = null; private String outputLanguage = null; @@ -39,6 +41,10 @@ public class PGFWrapper { private List listeners = new LinkedList(); + public PGFWrapper() { + this.baseURL = null; + this.pgf = new PGF(); + } public PGFWrapper(String baseURL) { this.baseURL = baseURL; @@ -93,7 +99,7 @@ public class PGFWrapper { protected void updateSelectedGrammar () { clearCachedInfo(); - pgf.grammar(baseURL+"/"+pgfName, new PGF.GrammarCallback() { + pgf.grammar(grammarURL, new PGF.GrammarCallback() { public void onResult(PGF.Grammar grammar) { userLanguage = grammar.getUserLanguage(); languages = new LinkedHashMap(); @@ -119,15 +125,27 @@ public class PGFWrapper { // public JSONRequest translate (String input, final PGF.TranslateCallback callback) { - return pgf.translate(baseURL+"/"+pgfName, input, inputLanguage, cat, outputLanguage, callback); + return pgf.translate(grammarURL, input, inputLanguage, cat, outputLanguage, callback); } public JSONRequest complete (String input, int limit, final PGF.CompleteCallback callback) { - return pgf.complete(baseURL+"/"+pgfName, input, inputLanguage, cat, limit, callback); + return pgf.complete(grammarURL, input, inputLanguage, cat, limit, callback); } public JSONRequest parse (String input, final PGF.ParseCallback callback) { - return pgf.parse(baseURL+"/"+pgfName, input, inputLanguage, cat, callback); + return pgf.parse(grammarURL, input, inputLanguage, cat, callback); + } + + public String graphvizAbstractTree(String abstractTree) { + return pgf.graphvizAbstractTree(grammarURL,abstractTree); + } + + public String graphvizParseTree(String abstractTree, String lang) { + return pgf.graphvizParseTree(grammarURL,abstractTree,lang); + } + + public String graphvizAlignment(String abstractTree) { + return pgf.graphvizAlignment(grammarURL,abstractTree); } // @@ -140,6 +158,20 @@ public class PGFWrapper { public void setPGFName(String pgfName) { this.pgfName = pgfName; + this.grammarURL = baseURL + "/" + pgfName; + this.inputLanguage = null; + this.outputLanguage = null; + this.cat = null; + updateSelectedGrammar(); + } + + public String getGrammarURL() { + return grammarURL; + } + + public void setGrammarURL(String grammarURL) { + this.pgfName = null; + this.grammarURL = grammarURL; this.inputLanguage = null; this.outputLanguage = null; this.cat = null; diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java index 2370d484c..14aa2da2d 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/client/TranslateApp.java @@ -6,10 +6,7 @@ import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.History; import com.google.gwt.user.client.HistoryListener; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.*; public class TranslateApp implements EntryPoint { @@ -47,7 +44,24 @@ public class TranslateApp implements EntryPoint { outputPanel.clear(); outputPanel.removeStyleDependentName("working"); for (PGF.Translation t : translations.iterable()) { - outputPanel.add(createTranslation(t.getTo(), t.getText())); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.addStyleName("my-translation-frame"); + VerticalPanel linsPanel = new VerticalPanel(); + linsPanel.addStyleName("my-translation-bar"); + hPanel.add(linsPanel); + HorizontalPanel btnPanel = new HorizontalPanel(); + btnPanel.addStyleName("my-translation-btns"); + btnPanel.setSpacing(4); + btnPanel.add(createAbsTreeButton(t.getTree())); + btnPanel.add(createAlignButton(t.getTree())); + hPanel.add(btnPanel); + hPanel.setCellHorizontalAlignment(btnPanel, + HasHorizontalAlignment.ALIGN_RIGHT); + outputPanel.add(hPanel); + + for (PGF.Linearization l : t.getLinearizations().iterable()) { + linsPanel.add(createTranslation(l.getTo(), t.getTree(), l.getText())); + } } } public void onError (Throwable e) { @@ -56,13 +70,113 @@ public class TranslateApp implements EntryPoint { }); } - protected Widget createTranslation(String language, String text) { + protected Widget createAbsTreeButton(final String abstractTree) { + Image treeBtn = new Image("se.chalmers.cs.gf.gwt.TranslateApp/tree-btn.png"); + treeBtn.addClickListener( + new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Abstract Syntax Tree"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + + Frame image = new Frame(pgf.graphvizAbstractTree(abstractTree)); + image.addStyleName("my-treeimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); + return treeBtn; + } + + protected Widget createAlignButton(final String abstractTree) { + Image alignBtn = new Image("se.chalmers.cs.gf.gwt.TranslateApp/align-btn.png"); + alignBtn.addClickListener( + new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Word Alignment"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + Frame image = new Frame(pgf.graphvizAlignment(abstractTree)); + image.addStyleName("my-alignmentimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); + return alignBtn; + } + + protected Widget createTranslation(final String language, final String abstractTree, String text) { Label l = new Label(text); l.addStyleName("my-translation"); String lang = pgf.getLanguageCode(language); if (lang != null) { l.getElement().setLang(lang); } + l.addClickListener(new ClickListener() { + public void onClick(Widget sender) { + // Create a dialog box and set the caption text + final DialogBox dialogBox = new DialogBox(); + dialogBox.setText("Parse Tree"); + + // Create a table to layout the content + HorizontalPanel dialogContents = new HorizontalPanel(); + dialogContents.setSpacing(4); + dialogBox.setWidget(dialogContents); + + // Add an image to the dialog + Frame image = new Frame(pgf.graphvizParseTree(abstractTree, language)); + image.addStyleName("my-treeimage"); + dialogContents.add(image); + + // Add a close button at the bottom of the dialog + Button closeButton = new Button("Close", + new ClickListener() { + public void onClick(Widget sender) { + dialogBox.hide(); + } + }); + dialogContents.add(closeButton); + + dialogBox.center(); + dialogBox.show(); + } + }); return l; } diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css index 9a904c228..90fcc75ea 100644 --- a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css +++ b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/Translate.css @@ -23,13 +23,40 @@ .my-translations { margin-top: 1em; } - + +.my-translation-frame { + width: 100%; + margin: 0.5em; + background: #D0E4F6; +} + +.my-translation-bar { + padding-left: 25px; + padding-right: 25px; + width: 100%; +} + .my-translation { margin: 0.2em; - padding-left: 25px; font-size: 150%; background-repeat: no-repeat; background-position: 0% 50%; + cursor:pointer; +} + +.my-translation-btns { + background: #DDDDDD; + cursor:pointer; +} + +.my-treeimage { + width: 650px; + height: 520px; +} + +.my-alignmentimage { + width: 450px; + height: 300px; } /* diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/align-btn.png b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/align-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..ca6a391c1357108a4891b3ad65579fac688fa75b GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0y~yU=UznVBqFpV_;w?d&FzWz`zjb>EaloG4bpqOTGgJ z94zU&|Nf^2MRp1*tU7U9F~Ne>VyDO(lXDDr{O6ZE`PG(M7#7kmx>_%i*I`fckwUiy t{S{YOF1!+V*y-?~Nqfb@e!Gv1QI7?r**jgfFfcGMc)I$ztaD0e0szI&E?WQq literal 0 HcmV?d00001 diff --git a/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/tree-btn.png b/src/server/gwt/src/se/chalmers/cs/gf/gwt/public/tree-btn.png new file mode 100644 index 0000000000000000000000000000000000000000..ebd243617c80e8493710e69b97236db55e7743bb GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0y~yU=UznVBqFpV_;w?d&FzWz`zjW>EaloF>&ogN1+1> z9L&z`|LZMnLszQlvKL=Gn8I$bz%{V@tcwya!;=)3oSTZ=Ke$3;_43b|Em&*S6g8(> zT6V2