3535import java .util .Properties ;
3636
3737/**
38- * R and SparkR interpreter.
38+ * R and SparkR interpreter with visualization support .
3939 */
4040public class SparkRInterpreter extends Interpreter {
4141 private static final Logger logger = LoggerFactory .getLogger (SparkRInterpreter .class );
@@ -54,14 +54,6 @@ public class SparkRInterpreter extends Interpreter {
5454 .add ("spark.home" ,
5555 SparkInterpreter .getSystemDefault ("SPARK_HOME" , "spark.home" , "/opt/spark" ),
5656 "Spark distribution location" )
57- .add ("zeppelin.R.result.width" ,
58- SparkInterpreter .getSystemDefault ("ZEPPELIN_R_PARAGRAPH_WIDTH" ,
59- "zeppelin.R.result.width" , "100%" ),
60- "" )
61- .add ("zeppelin.R.result.height" ,
62- SparkInterpreter .getSystemDefault ("ZEPPELIN_R_PARAGRAPH_HEIGHT" ,
63- "zeppelin.R.result.height" , "100%" ),
64- "" )
6557 .add ("zeppelin.R.image.width" ,
6658 SparkInterpreter .getSystemDefault ("ZEPPELIN_R_IMAGE_WIDTH" ,
6759 "zeppelin.R.image.width" , "100%" ),
@@ -90,32 +82,15 @@ public void open() {
9082 public InterpreterResult interpret (String lines , InterpreterContext contextInterpreter ) {
9183
9284 String imageWidth = getProperty ("zeppelin.R.image.width" );
93- String resultWidth = getProperty ("zeppelin.R.result.width" );
94- String resultHeight = getProperty ("zeppelin.R.result.height" );
95-
96- String widthScript =
97- "this.style.width = this.contentWindow.document.body.scrollWidth + 'px';" ;
98- String heightScript =
99- "this.style.height = this.contentWindow.document.body.scrollHeight + 'px';" ;
10085
10186 String [] sl = lines .split ("\n " );
10287 if (sl [0 ].contains ("{" ) && sl [0 ].contains ("}" )) {
10388 String jsonConfig = sl [0 ].substring (sl [0 ].indexOf ("{" ), sl [0 ].indexOf ("}" ) + 1 );
10489 ObjectMapper m = new ObjectMapper ();
10590 try {
10691 JsonNode rootNode = m .readTree (jsonConfig );
107- JsonNode resultWidthNode = rootNode .path ("resultWidth" );
108- if (!resultWidthNode .isMissingNode ()) {
109- resultWidth = resultWidthNode .textValue ();
110- widthScript = "" ;
111- }
112- JsonNode resultHeightNode = rootNode .path ("resultHeight" );
113- if (!resultHeightNode .isMissingNode ()) {
114- resultHeight = resultHeightNode .textValue ();
115- heightScript = "" ;
116- }
11792 JsonNode imageWidthNode = rootNode .path ("imageWidth" );
118- if (!imageWidthNode .isMissingNode ()) imageWidth = imageWidthNode .textValue ();
93+ if (! imageWidthNode .isMissingNode ()) imageWidth = imageWidthNode .textValue ();
11994 }
12095 catch (Exception e ) {
12196 logger .warn ("Can not parse json config: " + jsonConfig , e );
@@ -129,34 +104,14 @@ public InterpreterResult interpret(String lines, InterpreterContext contextInter
129104
130105 zeppelinR ().set (".zcmd" , "\n ```{r " + renderOptions + "}\n " + lines + "\n ```" );
131106 zeppelinR ().eval (".zres <- knit2html(text=.zcmd)" );
132- String htmlOut = zeppelinR ().getS0 (".zres" );
107+ String html = zeppelinR ().getS0 (".zres" );
133108
134- String scaledHtml = format (htmlOut , imageWidth );
109+ html = format (html , imageWidth );
135110
136- String html = "%html"
137- + " "
138- + "<iframe style=\" overflow:hidden;\" src=\" data:text/html;base64,"
139- + Base64 .encodeBase64String (
140- scaledHtml
141- .replaceAll ("src=\" //" , "src=\" http://" )
142- .replaceAll ("href=\" //" , "href=\" http://" )
143- .getBytes ("UTF-8" ))
144- + "\" "
145- + " "
146- + "frameborder=\" 0\" "
147- + " "
148- + "onload=\" "
149- + heightScript
150- + widthScript
151- + "\" "
152- + " "
153- + "width=\" " + resultWidth + "\" "
154- + " "
155- + "height=\" " + resultHeight + "\" "
156- + " "
157- + "/>" ;
158-
159- return new InterpreterResult (InterpreterResult .Code .SUCCESS , html );
111+ return new InterpreterResult (
112+ InterpreterResult .Code .SUCCESS ,
113+ InterpreterResult .Type .HTML ,
114+ html );
160115
161116 } catch (Exception e ) {
162117 logger .error ("Exception while connecting to R" , e );
@@ -167,21 +122,45 @@ public InterpreterResult interpret(String lines, InterpreterContext contextInter
167122 // Do nothing...
168123 }
169124 }
170-
171125 }
172126
127+ /*
128+ * Ensure we return proper HTML to be displayed in the Zeppelin UI.
129+ */
173130 private String format (String html , String imageWidth ) {
174- Document d = Jsoup .parse (html );
175- if ((d .getElementsByTag ("p" ).size () == 1 ) && (d .getElementsByTag ("img" ).size () == 0 )) {
176- html = html .replaceAll ("<p>" , "<pre>" ).replaceAll ("</p>" , "</pre>" );
177- }
131+
178132 Document document = Jsoup .parse (html );
179- document .getElementsByTag ("head" ).append (ZeppelinR .css ());
180- Elements images = document .getElementsByTag ("img" );
133+
134+ Element body = document .getElementsByTag ("body" ).get (0 );
135+ Elements images = body .getElementsByTag ("img" );
136+ Elements scripts = body .getElementsByTag ("script" );
137+ Elements paragraphs = body .getElementsByTag ("p" );
138+
139+ if ((paragraphs .size () == 1 )
140+ && (images .size () == 0 )
141+ && (scripts .size () == 0 )
142+ ) {
143+
144+ // We are here with a pure text output, let's keep format intact...
145+
146+ return html .substring (
147+ html .indexOf ("<body>" ) + 6 ,
148+ html .indexOf ("</body>" )
149+ )
150+ .replace ("<p>" , "<pre style='background-color: white; border: 0px;'>" )
151+ .replace ("</p>" , "</pre>" );
152+
153+ }
154+
155+ // OK, we have more than text...
156+
181157 for (Element image : images ) {
182158 image .attr ("width" , imageWidth );
183159 }
184- return document .toString ();
160+
161+ return body .html ()
162+ .replaceAll ("src=\" //" , "src=\" http://" )
163+ .replaceAll ("href=\" //" , "href=\" http://" );
185164 }
186165
187166 @ Override
@@ -234,8 +213,9 @@ protected static ZeppelinRFactory zeppelinR() {
234213 }
235214
236215 /**
237- * Java Factory to support tests with Mockito
238- * (mockito can not mock the zeppelinR final scala class).
216+ * Java Factory to support tests with Mockito.
217+ *
218+ * (Mockito can not mock the zeppelinR final scala object class).
239219 */
240220 protected static class ZeppelinRFactory {
241221 private static ZeppelinRFactory instance ;
0 commit comments