Author: bdelacretaz
Date: Tue Jun 17 08:41:57 2008
New Revision: 668714
URL: http://svn.apache.org/viewvc?rev=668714&view=rev
Log:
SLING-466 - JST template engine reworked, head and body elements are now
processed separately to generate the html rendering and the javascript
rendering code
Added:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
(with props)
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl
(with props)
Modified:
incubator/sling/trunk/parent/pom.xml
incubator/sling/trunk/scripting/jst/pom.xml
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/HtmlCodeGenerator.java
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JsCodeGenerator.java
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JstScriptEngine.java
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/ScriptFilteredCopy.java
Modified: incubator/sling/trunk/parent/pom.xml
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/parent/pom.xml?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
--- incubator/sling/trunk/parent/pom.xml (original)
+++ incubator/sling/trunk/parent/pom.xml Tue Jun 17 08:41:57 2008
@@ -692,8 +692,13 @@
<artifactId>pax-web-service</artifactId>
<version>0.2.3</version>
</dependency>
+
+ <dependency>
+ <groupId>nekohtml</groupId>
+ <artifactId>nekohtml</artifactId>
+ <version>1.9.6.2</version>
+ </dependency>
</dependencies>
-
</dependencyManagement>
Modified: incubator/sling/trunk/scripting/jst/pom.xml
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/pom.xml?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
--- incubator/sling/trunk/scripting/jst/pom.xml (original)
+++ incubator/sling/trunk/scripting/jst/pom.xml Tue Jun 17 08:41:57 2008
@@ -58,17 +58,26 @@
<extensions>true</extensions>
<configuration>
<instructions>
+ <!--
+ TODO the xerces and dom dependencies should
+ come from separate bundles
+ -->
<Private-Package>
org.apache.sling.scripting.jst,
org.apache.sling.scripting.javascript.io.*,
- org.apache.sling.servlets.get.helpers.*
+ org.apache.sling.servlets.get.helpers.*,
+ org.cyberneko.html.*,
+ org.apache.xerces.*,
+ org.w3c.dom.*,
+ org.apache.html.dom.*
</Private-Package>
<Import-Package>
org.apache.bsf.*;
sun.misc; resolution:=optional,
- *
+ javax.xml.*
</Import-Package>
+ <DynamicImport-Package>*</DynamicImport-Package>
<ScriptEngine-Name>${pom.name}</ScriptEngine-Name>
<ScriptEngine-Version>${pom.version}</ScriptEngine-Version>
@@ -129,6 +138,20 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>nekohtml</groupId>
+ <artifactId>nekohtml</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>xerces</groupId>
+ <artifactId>xercesImpl</artifactId>
+ <version>2.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>javax.jcr</groupId>
+ <artifactId>jcr</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
Added:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java?rev=668714&view=auto
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
(added)
+++
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
Tue Jun 17 08:41:57 2008
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.scripting.jst;
+
+import java.util.regex.Pattern;
+
+
+/** Extends ScriptFilteredCopy by copying only code that was
+ * generated from inside the <body> tag of the template
+ */
+class BodyOnlyScriptFilteredCopy extends ScriptFilteredCopy {
+
+ private int state = 0;
+ private final Pattern bodyStart;
+ private final Pattern bodyEnd;
+
+ BodyOnlyScriptFilteredCopy() {
+ bodyStart = Pattern.compile("^out.write\\(. *<body.*");
+ bodyEnd = Pattern.compile("^out.write\\(. *</body.*");
+ }
+
+ protected boolean copyLine(String line) {
+ boolean result = false;
+
+ if(state == 0) {
+ // <body> not seen yet, go to state 1 if seen
+ if(bodyStart.matcher(line).matches()) {
+ state = 1;
+ }
+
+ } else if(state == 1) {
+ // if </body>, done copying -> state 2
+ if(bodyEnd.matcher(line).matches()) {
+ state = 2;
+ } else {
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+}
Propchange:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/BodyOnlyScriptFilteredCopy.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision Rev URL
Modified:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/HtmlCodeGenerator.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/HtmlCodeGenerator.java?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/HtmlCodeGenerator.java
(original)
+++
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/HtmlCodeGenerator.java
Tue Jun 17 08:41:57 2008
@@ -18,12 +18,22 @@
*/
package org.apache.sling.scripting.jst;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
+import java.io.StringWriter;
import java.util.LinkedList;
import java.util.List;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
+import javax.script.ScriptException;
+import javax.xml.transform.Result;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
@@ -31,8 +41,11 @@
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.commons.json.jcr.JsonItemWriter;
import org.apache.sling.servlets.get.helpers.HtmlRendererServlet;
+import org.cyberneko.html.parsers.DOMParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.xml.sax.InputSource;
/** Generates HTML code for JST templates */
class HtmlCodeGenerator {
@@ -55,68 +68,86 @@
htmlRenderer = new HtmlRendererServlet();
}
- /** Generate HTML code for the given request and script path */
- void generateHtml(SlingHttpServletRequest request, String scriptPath,
PrintWriter output) throws RepositoryException, JSONException {
+ /** Generate HTML code for the given request and script path */
+ void generateHtml(SlingHttpServletRequest request, String scriptPath,
+ InputStream scriptStream, PrintWriter output)
+ throws RepositoryException, JSONException, ScriptException, IOException {
// access our data (need a Node)
final Resource r = request.getResource();
final Node n = r.adaptTo(Node.class);
-
- // output HEAD with javascript initializations
- // TODO we should instead parse (at least minimally) the template
file, and inject our
- // stuff in the right places
- output.println("<html><head><title id=\"JstPageTitle\">");
- output.println(getTitle(r, n));
- output.println("</title>");
-
- // TODO get additional head stuff from the script?
- // something like
- // <!-- jst:head
- // <link rel="stylesheet" href="/apps/foo/foo.css"/>
- // -->
-
- // library scripts
- for(String lib : libraryScripts) {
- final String fullScriptPath =
- SlingRequestPaths.getContextPath(request)
- + SlingRequestPaths.getServletPath(request)
- + lib
- ;
- output.println("<script type=\"text/javascript\" src=\"" +
fullScriptPath + "\"></script>");
+ final String pageTitle = getTitle(r, n);
+
+ // Parse script using the NekoHTML permissive HTML parser
+ final DOMParser parser = new DOMParser();
+ try {
+ parser.setFeature("http://xml.org/sax/features/namespaces",
false);
+
parser.setProperty("http://cyberneko.org/html/properties/names/elems", "lower");
+
parser.setProperty("http://cyberneko.org/html/properties/names/attrs", "lower");
+ parser.parse(new InputSource(scriptStream));
+ } catch(Exception e) {
+ final ScriptException se = new ScriptException("Error parsing
script " + scriptPath);
+ se.initCause(e);
+ throw se;
}
-
- // Node data in JSON format
- final JsonItemWriter j = new JsonItemWriter(null);
- final int maxRecursionLevels = 1;
- output.println("<script language='javascript'>");
- output.print("var currentNode=");
+ final Document template = parser.getDocument();
+
+ // compute default rendering
+ final StringWriter defaultRendering = new StringWriter();
if(n!=null) {
- j.dump(n, output, maxRecursionLevels);
- } else {
- output.print("{}");
+ final PrintWriter pw = new PrintWriter(defaultRendering);
+ htmlRenderer.render(pw, r, n);
+ pw.flush();
}
- output.println(";");
- output.println("</script>");
-
- // default rendering, turned off automatically from the javascript that
- // follows, if javascript is enabled
- output.println("</head><body>");
- output.println("<div id=\"JstDefaultRendering\">");
- if(n!=null) {
- htmlRenderer.render(output, r, n);
+
+ // compute currentNode values in JSON format
+ final StringWriter jsonData = new StringWriter();
+ if(n != null) {
+ final PrintWriter pw = new PrintWriter(jsonData);
+ final JsonItemWriter j = new JsonItemWriter(null);
+ final int maxRecursionLevels = 1;
+ pw.print("var currentNode=");
+ if(n!=null) {
+ j.dump(n, pw, maxRecursionLevels);
+ } else {
+ pw.print("{}");
+ }
+ pw.print(";");
+ pw.flush();
}
- output.println("</div>");
- output.println("<script language=\"javascript\">");
- output.println("var e =
document.getElementById(\"JstDefaultRendering\");
e.parentNode.removeChild(e);");
- output.println("</script>");
-
- // reference to script provided by the JstCodeGeneratorServlet
- final String scriptUrl = scriptPath + ".jst.js";
- output.println("<script type=\"text/javascript\" src=\"" + scriptUrl +
"\"></script>");
-
- // all done
- output.println("</body></html>");
+ // run XSLT transform on script, passing parameter
+ // for our computed values
+ final String xslt = "/xslt/script-transform.xsl";
+ InputStream xslTransform = getClass().getResourceAsStream(xslt);
+ if(xslTransform == null) {
+ throw new ScriptException("XSLT transform " + xslt + " not found");
+ }
+ try {
+ final TransformerFactory tf = TransformerFactory.newInstance();
+ final Transformer t = tf.newTransformer(new
StreamSource(xslTransform));
+ t.setParameter("pageTitle", pageTitle);
+ t.setParameter("slingScriptPath", fullPath(request,SLING_JS_PATH));
+ t.setParameter("jstScriptPath", fullPath(request, scriptPath +
".jst.js"));
+ t.setParameter("defaultRendering", defaultRendering.toString());
+ t.setParameter("jsonData", jsonData.toString());
+ final Result result = new StreamResult(output);
+ final DOMSource source = new DOMSource(template);
+ t.transform(source, result);
+
+ } catch (Exception e) {
+ final ScriptException se = new ScriptException("Error in XSLT
transform for " + scriptPath);
+ se.initCause(e);
+ throw se;
+
+ } finally {
+ xslTransform.close();
+ }
+ }
+
+ /** Return the full path to supplied resource */
+ static String fullPath (SlingHttpServletRequest request, String path) {
+ return SlingRequestPaths.getContextPath(request) +
SlingRequestPaths.getServletPath(request) + path;
}
/** Return the title to use for the generated page */
Modified:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JsCodeGenerator.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JsCodeGenerator.java?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JsCodeGenerator.java
(original)
+++
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JsCodeGenerator.java
Tue Jun 17 08:41:57 2008
@@ -31,12 +31,13 @@
class JsCodeGenerator {
/** Convert the script read on scriptReader to javascript code and
- * write the result to output using a ScriptFilteredCopy
+ * write the result to output using a BodyOnlyScriptFilteredCopy,
+ * to keep only the part that's relevant for the document body
*/
void generateCode(Reader scriptReader, PrintWriter output) throws
IOException {
final EspReader er = new EspReader(scriptReader);
- output.println("// Script generated by the Sling JST engine " +
getClass().getSimpleName());
- er.setOutInitStatement("out=document;\n");
- new ScriptFilteredCopy().copy(er,output);
+ er.setOutInitStatement("");
+ output.println("out=document;");
+ new BodyOnlyScriptFilteredCopy().copy(er,output);
}
}
Modified:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JstScriptEngine.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JstScriptEngine.java?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JstScriptEngine.java
(original)
+++
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/JstScriptEngine.java
Tue Jun 17 08:41:57 2008
@@ -19,6 +19,7 @@
package org.apache.sling.scripting.jst;
import java.io.IOException;
+import java.io.InputStream;
import java.io.Reader;
import javax.jcr.RepositoryException;
@@ -31,6 +32,8 @@
import org.apache.sling.api.scripting.SlingScriptHelper;
import org.apache.sling.commons.json.JSONException;
import org.apache.sling.scripting.api.AbstractSlingScriptEngine;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/** JST script engine.
* This engine does not really execute the supplied script: it dumps a default
@@ -42,7 +45,8 @@
public class JstScriptEngine extends AbstractSlingScriptEngine {
private final HtmlCodeGenerator htmlGenerator;
-
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
JstScriptEngine(ScriptEngineFactory scriptEngineFactory) {
super(scriptEngineFactory);
htmlGenerator = new HtmlCodeGenerator();
@@ -53,10 +57,12 @@
final Bindings props = context.getBindings(ScriptContext.ENGINE_SCOPE);
final SlingScriptHelper helper = (SlingScriptHelper)
props.get(SlingBindings.SLING);
+ final InputStream scriptStream =
helper.getScript().getScriptResource().adaptTo(InputStream.class);
try {
htmlGenerator.generateHtml(helper.getRequest(),
- helper.getScript().getScriptResource().getPath(),
helper.getResponse().getWriter());
+ helper.getScript().getScriptResource().getPath(),
scriptStream,
+ helper.getResponse().getWriter());
} catch (IOException ioe) {
throw new ScriptException(ioe);
@@ -66,9 +72,18 @@
} catch(JSONException je) {
throw new ScriptException(je);
+
+ } finally {
+ if(scriptStream != null) {
+ try {
+ scriptStream.close();
+ } catch(IOException ioe) {
+ log.warn("IOException while closing scriptStream",ioe);
+ }
+ }
}
return null;
}
-}
+}
\ No newline at end of file
Modified:
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/ScriptFilteredCopy.java
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/ScriptFilteredCopy.java?rev=668714&r1=668713&r2=668714&view=diff
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/ScriptFilteredCopy.java
(original)
+++
incubator/sling/trunk/scripting/jst/src/main/java/org/apache/sling/scripting/jst/ScriptFilteredCopy.java
Tue Jun 17 08:41:57 2008
@@ -37,11 +37,17 @@
final BufferedReader br = new BufferedReader(r);
String line = null;
while( (line = br.readLine()) != null) {
- final String toWrite = processLine(line);
- w.write(toWrite, 0, toWrite.length());
- w.write('\n');
+ if(copyLine(line)) {
+ final String toWrite = processLine(line);
+ w.write(toWrite, 0, toWrite.length());
+ w.write('\n');
+ }
}
}
+
+ protected boolean copyLine(String line) {
+ return true;
+ }
/** Transform lines that look like
* <pre>
Added:
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl
URL:
http://svn.apache.org/viewvc/incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl?rev=668714&view=auto
==============================================================================
---
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl
(added)
+++
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl
Tue Jun 17 08:41:57 2008
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+<!--
+ Transform JST scripts (after they've been parsed by nekoHTML) to
+ their HTML rendering, by adding some values passed via xsl:param
+ to the head element, and replacing the body with the default
+ rendering and a reference to the script that does the actual
+ client-side rendering.
+ -->
+<xsl:transform
+ version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+>
+
+ <!-- parameters provided by the HtmlCodeGenerator class -->
+ <xsl:param name="pageTitle"/>
+ <xsl:param name="jstScriptPath"/>
+ <xsl:param name="slingScriptPath"/>
+ <xsl:param name="defaultRendering"/>
+ <xsl:param name="jsonData"/>
+
+ <!--
+ NekoHTML should provide lowercase element names but
+ that doesn't seem to work
+ -->
+ <xsl:variable name="upper" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
+ <xsl:variable name="lower" select="'abcdefghijklmnopqrstuvwxyz'"/>
+
+ <!-- lowercase element names and call mode=inner templates -->
+ <xsl:template match="*">
+ <xsl:element name="{translate(local-name(),$upper,$lower)}">
+ <xsl:apply-templates select="@*"/>
+ <xsl:apply-templates mode="inner" select="."/>
+ </xsl:element>
+ </xsl:template>
+
+ <!-- copy attributes, lowercasing element names -->
+ <xsl:template match="@*">
+ <xsl:attribute name="{translate(local-name(),$upper,$lower)}">
+ <xsl:value-of select="."/>
+ </xsl:attribute>
+ </xsl:template>
+
+ <!-- by default, nothing special in mode=inner for -->
+ <xsl:template match="*" mode="inner">
+ <xsl:apply-templates/>
+ </xsl:template>
+
+ <!-- replace body with default rendering + script reference -->
+ <xsl:template match="body|BODY" mode="inner">
+ <div id="JstDefaultRendering">
+ <xsl:value-of select="$defaultRendering" disable-output-escaping="yes" />
+ </div>
+ <script language="javascript">
+ var e = document.getElementById("JstDefaultRendering");
+ e.parentNode.removeChild(e);
+ </script>
+ <script src="{$jstScriptPath}"/>
+ </xsl:template>
+
+ <!-- add reference to sling.js in head, and currentNode data -->
+ <xsl:template match="head|HEAD" mode="inner">
+ <xsl:apply-templates/>
+ <script src="{$slingScriptPath}"/>
+ <script language="javascript">
+ <xsl:value-of select="$jsonData"/>
+ </script>
+ </xsl:template>
+
+ <!-- For title, set value computed from node properties -->
+ <xsl:template match="head/title|HEAD/TITLE">
+ <title>
+ <xsl:value-of select="$pageTitle"/>
+ </title>
+ </xsl:template>
+
+</xsl:transform>
\ No newline at end of file
Propchange:
incubator/sling/trunk/scripting/jst/src/main/resources/xslt/script-transform.xsl
------------------------------------------------------------------------------
svn:eol-style = native