incubator-sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fmesc...@apache.org
Subject svn commit: r590999 [2/5] - in /incubator/sling/trunk/microsling: ./ microsling-core/ microsling-core/src/ microsling-core/src/main/ microsling-core/src/main/java/ microsling-core/src/main/java/org/ microsling-core/src/main/java/org/apache/ microsling-...
Date Thu, 01 Nov 2007 13:30:08 GMT
Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,354 @@
+/*
+ * 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.microsling.resource;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The microsling ResourceResolver locates a Resource using a
+ * ResourcePathIterator: it first tries to find an exact match for the Request
+ * URI, and if not goes up the path, breaking it a "." and "/", stopping if it
+ * finds a Resource.
+ */
+public class MicroslingResourceResolver implements ResourceResolver {
+
+    private final Logger log = LoggerFactory.getLogger(MicroslingResourceResolver.class);
+
+    private Session session;
+
+    public MicroslingResourceResolver(Session session) {
+        this.session = session;
+    }
+
+    public void dispose() {
+        if (session != null) {
+            try {
+                session.logout();
+            } catch (Throwable t) {
+                log.warn("dispose: Unexpected problem logging out", t);
+            }
+
+            session = null;
+        }
+    }
+
+    public Resource resolve(ServletRequest request) throws SlingException,
+            ResourceNotFoundException {
+        Resource result = null;
+        String path = null;
+        String pathInfo = ((HttpServletRequest) request).getPathInfo();
+        try {
+            Session session = getSession();
+            final ResourcePathIterator it = new ResourcePathIterator(pathInfo);
+            while (it.hasNext() && result == null) {
+                path = it.next();
+                if (log.isDebugEnabled()) {
+                    log.debug("Trying to locate Resource at path '" + path
+                        + "'");
+                }
+                result = getResource(session, path);
+            }
+        } catch (RepositoryException re) {
+            throw new SlingException("RepositoryException for path=" + path, re);
+        }
+
+        if (result == null) {
+            result = new NonExistingResource(pathInfo);
+        }
+
+        return result;
+    }
+
+    public Resource getResource(Resource base, String path)
+            throws SlingException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Resource getResource(String path) throws SlingException {
+        try {
+            Session session = getSession();
+            if (session.itemExists(path)) {
+                return getResource(session, path);
+            }
+
+            log.info("Path '{}' does not resolve to an Item", path);
+            return null;
+        } catch (RepositoryException re) {
+            throw new SlingException("Cannot get resource " + path, re);
+        }
+    }
+
+    public Iterator<Resource> listChildren(Resource parent)
+            throws SlingException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Iterator<Map<String, Object>> queryResources(String query,
+            String language) throws SlingException {
+        try {
+            QueryResult result = queryInternal(query, language);
+            final String[] colNames = result.getColumnNames();
+            final RowIterator rows = result.getRows();
+            return new Iterator<Map<String, Object>>() {
+                public boolean hasNext() {
+                    return rows.hasNext();
+                };
+
+                public Map<String, Object> next() {
+                    Map<String, Object> row = new HashMap<String, Object>();
+                    try {
+                        Value[] values = rows.nextRow().getValues();
+                        for (int i = 0; i < values.length; i++) {
+                            row.put(colNames[i], toJavaObject(values[i]));
+                        }
+                    } catch (RepositoryException re) {
+                        // TODO:log
+                    }
+                    return row;
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException("remove");
+                }
+            };
+        } catch (RepositoryException re) {
+            throw new SlingException(re);
+        }
+    }
+
+    public Iterator<Resource> findResources(String query, String language)
+            throws SlingException {
+        try {
+            final NodeIterator ni = queryInternal(query, language).getNodes();
+            return new Iterator<Resource>() {
+                public boolean hasNext() {
+                    return ni.hasNext();
+                }
+
+                public Resource next() {
+                    try {
+                        return new JcrNodeResource(ni.nextNode());
+                    } catch (RepositoryException re) {
+                        // log and return next, but not null
+                        return null;
+                    }
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException("remove");
+                }
+            };
+        } catch (RepositoryException re) {
+            throw new SlingException(re);
+        }
+    }
+
+    protected Session getSession() throws SlingException {
+        if (session != null && session.isLive()) {
+            return session;
+        }
+
+        throw new SlingException("Session has already been closed");
+    }
+
+    protected Resource getResource(Session session, String path)
+            throws RepositoryException {
+        if (session.itemExists(path)) {
+            Resource result = new JcrNodeResource(session, path);
+            result.getResourceMetadata().put(ResourceMetadata.RESOLUTION_PATH,
+                path);
+            log.info("Found Resource at path '{}'", path);
+            return result;
+        }
+
+        return null;
+    }
+
+    private QueryResult queryInternal(String query, String language)
+            throws RepositoryException, SlingException {
+        Session s = getSession();
+        Query q = s.getWorkspace().getQueryManager().createQuery(query,
+            language);
+        return q.execute();
+    }
+
+    private Object toJavaObject(Value value) throws RepositoryException {
+        switch (value.getType()) {
+            case PropertyType.BINARY:
+                return new LazyInputStream(value);
+            case PropertyType.BOOLEAN:
+                return value.getBoolean();
+            case PropertyType.DATE:
+                return value.getDate();
+            case PropertyType.DOUBLE:
+                return value.getDouble();
+            case PropertyType.LONG:
+                return value.getLong();
+            case PropertyType.NAME: // fall through
+            case PropertyType.PATH: // fall through
+            case PropertyType.REFERENCE: // fall through
+            case PropertyType.STRING: // fall through
+            case PropertyType.UNDEFINED: // not actually expected
+            default: // not actually expected
+                return value.getString();
+        }
+    }
+
+    private static class LazyInputStream extends InputStream {
+
+        private final Value value;
+
+        private InputStream delegatee;
+
+        LazyInputStream(Value value) {
+            this.value = value;
+        }
+
+        private InputStream getStream() throws IOException {
+            if (delegatee == null) {
+                try {
+                    delegatee = value.getStream();
+                } catch (RepositoryException re) {
+                    throw (IOException) new IOException(re.getMessage()).initCause(re);
+                }
+            }
+            return delegatee;
+        }
+
+        @Override
+        public void close() throws IOException {
+            if (delegatee != null) {
+                delegatee.close();
+            }
+        }
+
+        @Override
+        public int available() throws IOException {
+            return getStream().available();
+        }
+
+        @Override
+        public int read() throws IOException {
+            return getStream().read();
+        }
+
+        @Override
+        public int read(byte[] b) throws IOException {
+            return getStream().read(b);
+        }
+
+        @Override
+        public int read(byte[] b, int off, int len) throws IOException {
+            return getStream().read(b, off, len);
+        }
+
+        @Override
+        public long skip(long n) throws IOException {
+            return getStream().skip(n);
+        }
+
+        @Override
+        public boolean markSupported() {
+            try {
+                return getStream().markSupported();
+            } catch (IOException ioe) {
+                // TODO: log
+            }
+            return false;
+        }
+
+        @Override
+        public synchronized void mark(int readlimit) {
+            try {
+                getStream().mark(readlimit);
+            } catch (IOException ioe) {
+                // TODO: log
+            }
+        }
+
+        @Override
+        public synchronized void reset() throws IOException {
+            getStream().reset();
+        }
+    }
+
+    /**
+     * Resource class used to represent a non existing resource returned by the
+     * {@link MicroslingResourceResolver#resolve(ServletRequest)} method.
+     */
+    private static class NonExistingResource implements Resource {
+
+        /** The path of the missing resource */
+        private final String resourcePath;
+
+        /** The reource meta data just containing the resource path again */
+        private final ResourceMetadata resourceMetadata;
+
+        NonExistingResource(String resourcePath) {
+            this.resourcePath = resourcePath;
+
+            resourceMetadata = new MicroslingResourceMetadata();
+            resourceMetadata.put(ResourceMetadata.RESOLUTION_PATH, resourcePath);
+        }
+
+        public String getURI() {
+            return resourcePath;
+        }
+
+        public String getResourceType() {
+            return RESOURCE_TYPE_NON_EXISTING;
+        }
+
+        public ResourceMetadata getResourceMetadata() {
+            return resourceMetadata;
+        }
+
+        public Object getObject() {
+            return null;
+        }
+
+        public Object getRawData() {
+            return null;
+        }
+
+    }
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/MicroslingResourceResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,70 @@
+/*
+ * 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.microsling.resource;
+
+import java.util.Iterator;
+
+/** Iterate over the the HTTP request path by creating shorter segments
+ *  of that path using "." and "/" as separators.
+ *  
+ *  For example, if path = /some/stuff.a4.html/xyz the sequence is:
+ *  <ol>
+ *    <li>
+ *      /some/stuff.a4.html/xyz
+ *    </li>
+ *    <li>
+ *      /some/stuff.a4.html
+ *    </li>
+ *    <li>
+ *      /some/stuff.a4
+ *    </li>
+ *    <li>
+ *      /some/stuff
+ *    </li>
+ *    <li>
+ *      /some
+ *    </li>
+ *  </ol>
+ */
+class ResourcePathIterator implements Iterator<String> {
+
+    private String nextPath;
+    
+    ResourcePathIterator(String path) {
+        nextPath = path;
+    }
+    
+    public boolean hasNext() {
+        return nextPath != null && nextPath.length() > 0;
+    }
+
+    public String next() {
+        final String result = nextPath;
+        final int pos = Math.max(result.lastIndexOf('.'),result.lastIndexOf('/'));
+        if(pos < 0) {
+            nextPath = null;
+        } else {
+            nextPath = result.substring(0,pos);
+        }
+        return result;
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException("Cannot remove() on ResourcePathIterator");
+    }
+
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/resource/ResourcePathIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptResolver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptResolver.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptResolver.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptResolver.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,368 @@
+/*
+ * 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.microsling.scripting;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.servlet.ServletException;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptEngine;
+import org.apache.sling.api.scripting.SlingScriptResolver;
+import org.apache.sling.microsling.scripting.engines.rhino.RhinoJavasSriptEngine;
+import org.apache.sling.microsling.scripting.engines.velocity.VelocityTemplatesScriptEngine;
+import org.apache.sling.microsling.scripting.helpers.ScriptFilenameBuilder;
+import org.apache.sling.microsling.scripting.helpers.ScriptHelper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Find scripts in the repository, based on the current Resource type. The
+ * script filename is built using the current HTTP request method name, followed
+ * by the extension of the current request and the desired script extension. For
+ * example, a "js" script for a GET request on a Resource of type some/type with
+ * request extension "html" should be stored as
+ *
+ * <pre>
+ *      /sling/scripts/some/type/get.html.js
+ * </pre>
+ *
+ * in the repository.
+ */
+public class MicroslingScriptResolver implements SlingScriptResolver {
+
+    private static final Logger log = LoggerFactory.getLogger(MicroslingScriptResolver.class);
+
+    public static final String SCRIPT_BASE_PATH = "/sling/scripts";
+
+    /**
+     * jcr:encoding
+     */
+    public static final String JCR_ENCODING = "jcr:encoding";
+
+    private final ScriptFilenameBuilder scriptFilenameBuilder = new ScriptFilenameBuilder();
+
+    private Map<String, SlingScriptEngine> scriptEngines;
+
+    public MicroslingScriptResolver() throws SlingException {
+        scriptEngines = new HashMap<String, SlingScriptEngine>();
+        addScriptEngine(new RhinoJavasSriptEngine());
+        addScriptEngine(new VelocityTemplatesScriptEngine());
+    }
+
+    /**
+     * @param req
+     * @param scriptExtension
+     * @return <code>true</code> if a MicroslingScript and a ScriptEngine to
+     *         evaluate it could be found. Otherwise <code>false</code> is
+     *         returned.
+     * @throws ServletException
+     * @throws IOException
+     */
+    public static void evaluateScript(final SlingScript script, final SlingHttpServletRequest req,
+            final SlingHttpServletResponse resp) throws ServletException,
+            IOException {
+        try {
+            // the script helper
+            ScriptHelper helper = new ScriptHelper(req, resp);
+
+            // prepare the properties for the script
+            Map<String, Object> props = new HashMap<String, Object>();
+            props.put(SlingScriptEngine.SLING, helper);
+            props.put(SlingScriptEngine.REQUEST, helper.getRequest());
+            props.put(SlingScriptEngine.RESPONSE, helper.getResponse());
+            props.put(SlingScriptEngine.OUT, helper.getResponse().getWriter());
+            props.put(SlingScriptEngine.LOG, LoggerFactory.getLogger(script.getScriptPath()));
+
+            resp.setContentType(req.getResponseContentType()
+                + "; charset=UTF-8");
+
+            // evaluate the script now using the ScriptEngine
+            script.getScriptEngine().eval(script, props);
+
+            // ensure data is flushed to the underlying writer in case
+            // anything has been written
+            helper.getResponse().getWriter().flush();
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (ServletException se) {
+            throw se;
+        } catch (Exception e) {
+            throw new SlingException("Cannot get MicroslingScript: "
+                + e.getMessage(), e);
+        }
+    }
+
+    public SlingScript findScript(String path) throws SlingException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    /**
+     * Try to find a script Node that can process the given request, based on
+     * the rules defined above.
+     *
+     * @return null if not found.
+     */
+    public SlingScript resolveScript(final SlingHttpServletRequest request)
+            throws SlingException {
+        try {
+            return resolveScriptInternal(request);
+        } catch (RepositoryException re) {
+            throw new SlingException("Cannot resolve script for request", re);
+        }
+    }
+
+    public SlingScript resolveScriptInternal(
+            final SlingHttpServletRequest request) throws RepositoryException {
+
+        final Resource r = request.getResource();
+
+        // ensure repository access
+        if (!(r.getRawData() instanceof Item)) {
+            return null;
+        }
+
+        final Session s = ((Item) r.getRawData()).getSession();
+        MicroslingScript result = null;
+
+        if (r == null) {
+            return null;
+        }
+
+        String scriptFilename = scriptFilenameBuilder.buildScriptFilename(
+            request.getMethod(),
+            request.getRequestPathInfo().getSelectorString(),
+            request.getResponseContentType(), "*");
+        String scriptPath = SCRIPT_BASE_PATH + "/" + r.getResourceType();
+
+        // SLING-72: if the scriptfilename contains a relative path, move that
+        // to the scriptPath and make the scriptFilename a direct child pattern
+        int lastSlash = scriptFilename.lastIndexOf('/');
+        if (lastSlash >= 0) {
+            scriptPath += "/" + scriptFilename.substring(0, lastSlash);
+            scriptFilename = scriptFilename.substring(lastSlash + 1);
+        }
+
+        // this is the location of the trailing asterisk
+        final int scriptExtensionOffset = scriptFilename.length() - 1;
+
+        if (log.isDebugEnabled()) {
+            log.debug("Looking for script with filename=" + scriptFilename
+                + " under " + scriptPath);
+        }
+
+        if (s.itemExists(scriptPath)) {
+
+            // get the item and ensure it is a node
+            final Item i = s.getItem(scriptPath);
+            if (i.isNode()) {
+                Node parent = (Node) i;
+                NodeIterator scriptNodeIterator = parent.getNodes(scriptFilename);
+                while (scriptNodeIterator.hasNext()) {
+                    Node scriptNode = scriptNodeIterator.nextNode();
+
+                    // SLING-72: Require the node to be an nt:file
+                    if (scriptNode.isNodeType("nt:file")) {
+
+                        String scriptName = scriptNode.getName();
+                        String scriptExt = scriptName.substring(scriptExtensionOffset);
+                        SlingScriptEngine scriptEngine = scriptEngines.get(scriptExt);
+
+                        if (scriptEngine != null) {
+                            MicroslingScript script = new MicroslingScript();
+                            script.setNode(scriptNode);
+                            script.setScriptPath(scriptNode.getPath());
+                            script.setScriptEngine(scriptEngine);
+                            result = script;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (result != null) {
+            log.info("Found nt:file script node " + result.getScriptPath()
+                + " for Resource=" + r);
+        } else {
+            log.debug("nt:file script node not found at path=" + scriptPath
+                + " for Resource=" + r);
+        }
+
+        return result;
+    }
+
+    protected String filterStringForFilename(String inputString) {
+        final StringBuffer sb = new StringBuffer();
+        final String str = inputString.toLowerCase();
+        for (int i = 0; i < str.length(); i++) {
+            final char c = str.charAt(i);
+            if (Character.isLetterOrDigit(c)) {
+                sb.append(c);
+            } else {
+                sb.append("_");
+            }
+        }
+        return sb.toString();
+    }
+
+    private void addScriptEngine(SlingScriptEngine scriptEngine) {
+        String[] extensions = scriptEngine.getExtensions();
+        for (String extension : extensions) {
+            scriptEngines.put(extension, scriptEngine);
+        }
+    }
+
+    private static class MicroslingScript implements SlingScript {
+        private Node node;
+
+        private String scriptPath;
+
+        private SlingScriptEngine scriptEngine;
+
+        /**
+         * @return the node
+         */
+        Node getNode() {
+            return node;
+        }
+
+        /**
+         * @param node the node to set
+         */
+        void setNode(Node node) {
+            this.node = node;
+        }
+
+        /**
+         * @return the scriptPath
+         */
+        public String getScriptPath() {
+            return scriptPath;
+        }
+
+        /**
+         * @param scriptPath the scriptPath to set
+         */
+        void setScriptPath(String scriptPath) {
+            this.scriptPath = scriptPath;
+        }
+
+        /**
+         * @return the scriptEngine
+         */
+        public SlingScriptEngine getScriptEngine() {
+            return scriptEngine;
+        }
+
+        /**
+         * @param scriptEngine the scriptEngine to set
+         */
+        void setScriptEngine(SlingScriptEngine scriptEngine) {
+            this.scriptEngine = scriptEngine;
+        }
+
+        /**
+         * @return The script stored in the node as a Reader
+         */
+        public Reader getScriptReader() throws IOException {
+
+            Property property;
+            Value value;
+
+            try {
+                // SLING-72: Cannot use primary items due to WebDAV creating
+                // nt:unstructured as jcr:content node. So we just assume
+                // nt:file and try to use the well-known data path
+                property = getNode().getProperty("jcr:content/jcr:data");
+                value = null;
+                if (property.getDefinition().isMultiple()) {
+                    // for a multi-valued property, we take the first non-null
+                    // value (null values are possible in multi-valued
+                    // properties)
+                    // TODO: verify this claim ...
+                    Value[] values = property.getValues();
+                    for (Value candidateValue : values) {
+                        if (candidateValue != null) {
+                            value = candidateValue;
+                            break;
+                        }
+                    }
+
+                    // incase we could not find a non-null value, we bail out
+                    if (value == null) {
+                        throw new IOException("Cannot access "
+                            + getScriptPath());
+                    }
+                } else {
+                    // for single-valued properties, we just take this value
+                    value = property.getValue();
+                }
+            } catch (RepositoryException re) {
+                throw (IOException) new IOException("Cannot get script "
+                    + getScriptPath()).initCause(re);
+            }
+
+            // Now know how to get the input stream, we still have to decide
+            // on the encoding of the stream's data. Primarily we assume it is
+            // UTF-8, which is a default in many places in JCR. Secondarily
+            // we try to get a jcr:encoding property besides the data property
+            // to provide a possible encoding
+            String encoding = "UTF-8";
+            try {
+                Node parent = property.getParent();
+                if (parent.hasNode(JCR_ENCODING)) {
+                    encoding = parent.getProperty(JCR_ENCODING).getString();
+                }
+            } catch (RepositoryException re) {
+                // don't care if we fail for any reason here, just assume
+                // default
+            }
+
+            // access the value as a stream and return a buffered reader
+            // converting the stream data using UTF-8 encoding, which is
+            // the default encoding used
+            try {
+                InputStream input = value.getStream();
+                return new BufferedReader(
+                    new InputStreamReader(input, encoding));
+            } catch (RepositoryException re) {
+                throw (IOException) new IOException("Cannot get script "
+                    + getScriptPath()).initCause(re);
+            }
+        }
+    }
+
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptResolver.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptServlet.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptServlet.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptServlet.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptServlet.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,57 @@
+/*
+ * $Url: $
+ * $Id: $
+ *
+ * Copyright 1997-2005 Day Management AG
+ * Barfuesserplatz 6, 4001 Basel, Switzerland
+ * All Rights Reserved.
+ *
+ * This software is the confidential and proprietary information of
+ * Day Management AG, ("Confidential Information"). You shall not
+ * disclose such Confidential Information and shall use it only in
+ * accordance with the terms of the license agreement you entered into
+ * with Day.
+ */
+package org.apache.sling.microsling.scripting;
+
+import java.io.IOException;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.scripting.SlingScript;
+
+public class MicroslingScriptServlet implements Servlet {
+
+    private final SlingScript script;
+
+    public MicroslingScriptServlet(SlingScript script) {
+        this.script = script;
+    }
+
+    public void service(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException {
+        MicroslingScriptResolver.evaluateScript(script,
+            (SlingHttpServletRequest) req, (SlingHttpServletResponse) res);
+    }
+
+    public ServletConfig getServletConfig() {
+        return null;
+    }
+
+    public String getServletInfo() {
+        return "Servlet for script " + script.getScriptPath();
+    }
+
+    public void init(ServletConfig config) {
+    }
+
+    public void destroy() {
+    }
+
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/MicroslingScriptServlet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,85 @@
+/*
+ * 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.microsling.scripting.engines.freemarker;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.HttpStatusCodeException;
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptEngine;
+
+import freemarker.template.Configuration;
+import freemarker.template.Template;
+
+/**
+ * A ScriptEngine that uses {@link http://freemarker.org/ FreeMarker} templates
+ * to render a Resource in HTML.
+ */
+public class FreemarkerScriptEngine implements SlingScriptEngine {
+
+    public final static String FREEMARKER_SCRIPT_EXTENSION = "ftl";
+
+    private final Configuration configuration;
+
+    public FreemarkerScriptEngine() {
+        configuration = new Configuration();
+    }
+
+    public String[] getExtensions() {
+        return new String[] { FREEMARKER_SCRIPT_EXTENSION };
+    }
+
+    public String getEngineName() {
+        return "Freemarker Script Engine";
+    }
+
+    public String getEngineVersion() {
+        return "0.9";
+    }
+
+    public void eval(SlingScript script, Map<String, Object> props)
+            throws SlingException, IOException {
+
+        String scriptName = script.getScriptPath();
+        // ensure get method
+        HttpServletRequest request = (HttpServletRequest) props.get(REQUEST);
+        if (!"GET".equals(request.getMethod())) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED,
+                "FreeMarker templates only support GET requests");
+        }
+
+        Template tmpl = new Template(scriptName, script.getScriptReader(),
+            configuration);
+
+        try {
+            Writer w = ((HttpServletResponse) props.get(RESPONSE)).getWriter();
+            tmpl.process(props, w);
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            throw new SlingException("Failure running FreeMarker script "
+                + scriptName, t);
+        }
+    }
+}
\ No newline at end of file

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/freemarker/FreemarkerScriptEngine.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,120 @@
+/*
+ * 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.microsling.scripting.engines.rhino;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptEngine;
+import org.apache.sling.microsling.scripting.helpers.EspReader;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * A ScriptEngine that uses the Rhino interpreter to process Sling requests with
+ * server-side javascript.
+ */
+public class RhinoJavasSriptEngine implements SlingScriptEngine {
+
+    public final static String JS_SCRIPT_EXTENSION = "js";
+
+    public final static String ESP_SCRIPT_EXTENSION = "esp";
+
+    private String engineName;
+
+    {
+        Context cx = Context.enter();
+        engineName = "ECMAScript Engine (" + cx.getImplementationVersion() + ")";
+        Context.exit();
+    }
+
+    public String[] getExtensions() {
+        return new String[] { JS_SCRIPT_EXTENSION, ESP_SCRIPT_EXTENSION };
+    }
+
+    public String getEngineName() {
+        return engineName;
+    }
+
+    public String getEngineVersion() {
+        return "0.9";
+    }
+
+    public void eval(SlingScript script, Map<String, Object> props)
+            throws SlingException, IOException {
+
+        // wrap the reader in an EspReader for ESP scripts
+        Reader scriptReader = script.getScriptReader();
+        if (script.getScriptPath().endsWith(ESP_SCRIPT_EXTENSION)) {
+            scriptReader = new EspReader(scriptReader);
+        }
+
+        // create a rhino Context and execute the script
+        try {
+            final Context rhinoContext = Context.enter();
+            final ScriptableObject scope = rhinoContext.initStandardObjects();
+
+            // add initial properties to the scope
+            for (Entry<String, Object> entry : props.entrySet()) {
+                Object wrapped = wrap(scope, entry.getValue(), rhinoContext);
+                ScriptableObject.putProperty(scope, entry.getKey(), wrapped);
+            }
+
+            final int lineNumber = 1;
+            final Object securityDomain = null;
+
+            rhinoContext.evaluateReader(scope, scriptReader,
+                script.getScriptPath(), lineNumber, securityDomain);
+
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            throw new SlingException("Failure running script "
+                + script.getScriptPath(), t);
+        } finally {
+            Context.exit();
+        }
+    }
+
+    /**
+     * Wraps Java objects in Rhino host objects.
+     *
+     * @param scope the current scope
+     * @param entry the object to wrap
+     * @param context the current context, if needed for class definitions
+     * @return a rhino hostobject
+     */
+    protected Object wrap(final ScriptableObject scope, Object entry,
+            Context context) {
+        // wrap the resource to make it friendlier for javascript
+        if (entry instanceof Resource) {
+            try {
+                ScriptableObject.defineClass(scope, ScriptableResource.class);
+                return context.newObject(scope, "Resource",
+                    new Object[] { entry });
+            } catch (Exception e) {
+                return Context.javaToJS(entry, scope);
+            }
+        }
+        return Context.javaToJS(entry, scope);
+    }
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/RhinoJavasSriptEngine.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,150 @@
+/*
+ * 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.microsling.scripting.engines.rhino;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.NativeArray;
+import org.mozilla.javascript.ScriptRuntime;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+/**
+ * A wrapper for JCR nodes that exposes all properties and child nodes
+ * as properties of a Javascript object.
+ */
+public class ScriptableNode extends ScriptableObject {
+	private Node node;
+	
+	public ScriptableNode(Node item) {
+		super();
+		this.node = item;
+	}
+	
+	public String getClassName() {
+		return "Node";
+	}
+	/**
+	 * Gets the value of a (Javascript) property. If there is a single single-value
+	 * JCR property of this node, return its string value. If there are multiple properties
+	 * of the same name or child nodes of the same name, return an array.
+	 */
+	@Override
+	public Object get(String name, Scriptable start) {
+		Set items = new HashSet();
+		try {
+			Iterator it = node.getNodes(name);
+			while (it.hasNext()) {
+				items.add(new ScriptableNode((Node) it.next()));
+			}
+		} catch (RepositoryException e) {}
+		
+		try {
+			Iterator it = node.getProperties(name);
+			while (it.hasNext()) {
+				Property prop = (Property) it.next();
+				int type = prop.getType();
+				if (prop.getDefinition().isMultiple()) {
+					Value[] values = prop.getValues();
+					for (int i=0;i<values.length;i++) {
+						items.add(wrap(values[i], type));
+					}
+				} else {
+					if (type==PropertyType.REFERENCE) {
+						items.add(new ScriptableNode(prop.getNode()));
+					} else {
+						items.add(wrap(prop.getValue(), type));
+					}
+				}
+			}
+		} catch (RepositoryException e) {}
+		
+		if (items.size()==0) {
+			return null;
+		} else if (items.size()==1) {
+			return items.iterator().next();
+		} else {
+			//TODO: add write support
+			NativeArray result = new NativeArray(items.toArray());
+	        ScriptRuntime.setObjectProtoAndParent(result, this);
+	        return result;
+		}
+	}
+	private Object wrap(Value value, int type) throws ValueFormatException, IllegalStateException, RepositoryException {
+		if (type==PropertyType.BINARY) {
+			return Context.toBoolean(value.getBoolean());
+		} else if (type==PropertyType.DOUBLE) {
+			return Context.toNumber(value.getDouble());
+		} else if (type==PropertyType.LONG) {
+			return Context.toNumber(value.getLong());
+		}
+		return value.getString();
+	}
+
+	@Override
+	public Object[] getIds() {
+		Collection<String> ids = new HashSet<String>();
+		try {
+			PropertyIterator pit = node.getProperties();
+			while (pit.hasNext()) {
+				ids.add(pit.nextProperty().getName());
+			}
+		} catch (RepositoryException e) {
+			//do nothing, just do not list properties
+		}
+		try {
+			NodeIterator nit = node.getNodes();
+			while (nit.hasNext()) {
+				ids.add(nit.nextNode().getName());
+			}
+		} catch (RepositoryException e) {
+			//do nothing, just do not list child nodes
+		}
+		return ids.toArray();
+	}
+
+	@Override
+	public boolean has(String name, Scriptable start) {
+		try {
+			return node.hasProperty(name) || node.hasNode(name);
+		} catch (RepositoryException e) {
+			return false;
+		}
+	}
+
+	@Override
+	public Object getDefaultValue(Class typeHint) {
+		try {
+			return node.getPath();
+		} catch (RepositoryException e) {
+			return null;
+		}
+	}
+
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,97 @@
+/*
+ * 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.microsling.scripting.engines.rhino;
+
+import javax.jcr.Node;
+
+import org.apache.sling.api.resource.Resource;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ScriptableObject;
+import org.mozilla.javascript.Undefined;
+
+/**
+ * Resource in JavaScript has following signature: [Object] getData(); [Object]
+ * data [Item] getItem(); [Item] item [String] getResourceType(); [String] type
+ * [String] getURI(); [String] uri
+ */
+public class ScriptableResource extends ScriptableObject {
+    private Resource resource;
+
+    public ScriptableResource() {
+    }
+
+    public void jsConstructor(Object res) {
+        this.resource = (Resource) res;
+    }
+
+    @Override
+    public String getClassName() {
+        return "Resource";
+    }
+
+    public Object jsFunction_getObject() {
+        return resource.getObject();
+    }
+
+    public Object jsFunction_getRawData() {
+        return resource.getRawData();
+    }
+
+    public Object jsGet_item() {
+        if (resource.getRawData() instanceof Node) {
+            return new ScriptableNode((Node) resource.getRawData());
+        } else if (resource.getRawData() != null) {
+            return Context.javaToJS(resource.getRawData(), this);
+        } else {
+            return Undefined.instance;
+        }
+    }
+
+    public String jsFunction_getResourceType() {
+        return resource.getResourceType();
+    }
+
+    public String jsGet_type() {
+        return this.jsFunction_getResourceType();
+    }
+
+    public Object jsFunction_getURI() {
+        return Context.javaToJS(resource.getURI(), this);
+    }
+
+    public Object jsGet_uri() {
+        return this.jsFunction_getURI();
+    }
+
+    public Object jsFunction_getMetadata() {
+        return resource.getResourceMetadata();
+    }
+
+    public Object jsGet_meta() {
+        return resource.getResourceMetadata();
+    }
+
+    @Override
+    public Object getDefaultValue(Class typeHint) {
+        return resource.getURI();
+    }
+
+    public void setResource(Resource entry) {
+        this.resource = entry;
+    }
+
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/rhino/ScriptableResource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,94 @@
+/*
+ * 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.microsling.scripting.engines.velocity;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.HttpStatusCodeException;
+import org.apache.sling.api.SlingException;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptEngine;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+
+/**
+ * A ScriptEngine that uses Velocity templates to render a Resource
+ */
+public class VelocityTemplatesScriptEngine implements SlingScriptEngine {
+
+    public final static String VELOCITY_SCRIPT_EXTENSION = "vlt";
+
+    private final VelocityEngine velocity;
+
+    public VelocityTemplatesScriptEngine() throws SlingException {
+        velocity = new VelocityEngine();
+        try {
+            velocity.init();
+        } catch (Exception e) {
+            throw new SlingException("Exception in Velocity.init() "
+                + e.getMessage(), e);
+        }
+    }
+
+    public String[] getExtensions() {
+        return new String[] { VELOCITY_SCRIPT_EXTENSION };
+    }
+
+    public String getEngineName() {
+        return "Velocity Template Script Engine";
+    }
+
+    public String getEngineVersion() {
+        return "0.9";
+    }
+
+    public void eval(SlingScript script, Map<String, Object> props)
+            throws SlingException, IOException {
+
+        // ensure get method
+        HttpServletRequest request = (HttpServletRequest) props.get(REQUEST);
+        if (!"GET".equals(request.getMethod())) {
+            throw new HttpStatusCodeException(
+                HttpServletResponse.SC_METHOD_NOT_ALLOWED,
+                "Velocity templates only support GET requests");
+        }
+
+        // initialize the Velocity context
+        final VelocityContext c = new VelocityContext();
+        for (Entry<String, Object> entry : props.entrySet()) {
+            c.put(entry.getKey(), entry.getValue());
+        }
+
+        // let Velocity evaluate the script, and send the output to the browser
+        final String logTag = getClass().getSimpleName();
+        try {
+            Writer w = ((HttpServletResponse) props.get(RESPONSE)).getWriter();
+            velocity.evaluate(c, w, logTag, script.getScriptReader());
+        } catch (IOException ioe) {
+            throw ioe;
+        } catch (Throwable t) {
+            throw new SlingException("Failure running script "
+                + script.getScriptPath(), t);
+        }
+    }
+}

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/engines/velocity/VelocityTemplatesScriptEngine.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java?rev=590999&view=auto
==============================================================================
--- incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java (added)
+++ incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java Thu Nov  1 06:30:00 2007
@@ -0,0 +1,758 @@
+/*
+ * 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.microsling.scripting.helpers;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.util.Stack;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>EspReader</code> is a <code>FilterReader</code> which takes
+ * JSP like input and produces plain ECMA script output. The filtering
+ * modifications done on the input comprise the following :
+ * <ul>
+ * <li>Template text (HTML) is wrapped by out.write(). At most one line of
+ * text is wrapped into a single write() call. Double quote characters in the
+ * template text (e.g. for HTML tag attribute values) are escaped.
+ * <li>ECMA code is written to the output as is.
+ * <li>ECMA slash star (/*) comments are also written as is.
+ * <li>ECMA slash slash (//) comments are written as is.
+ * <li>JSP style template comments (<%-- -->) are also removed from the
+ * stream. Lineendings (LFs and CRLFs) are written, though.
+ * <li>HTML comments (<!-- -->) are not treated specially. Rather they are
+ * handled as plain template text written to the output wrapped in
+ * out.write(). The consequence of this behavious is, that as in JSP ECMA
+ * expressions may be included within the comments.
+ * </ul>
+ * <p>
+ * The nice thing about this reader is, that the line numbers of the resulting
+ * stream match the line numbers of the matching contents of the input stream.
+ * Due to the insertion of write() calls, column numbers will not necessarily
+ * match, though. This is especially true if you mix ECMA code tags (<% %>)
+ * with template text on the same line.
+ * <p>
+ * For maximum performance it is advisable to not create the EspReader with a
+ * plain FileReader or InputStreamReader but rather with a BufferedReader based
+ * on one of the simpler Readers. The reasons for this is, that we call the base
+ * reader character by character. This in turn is not too performing if the base
+ * reader does not buffer its input.
+ */
+public class EspReader extends FilterReader {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(EspReader.class);
+
+    /**
+     * Default parser state. This is the state the parser starts running in. In
+     * this state all text is treated as template text, which should be wrapped
+     * by out.write() line by line.
+     */
+    private static final byte PARSE_STATE_ESP = 1;
+
+    /**
+     * ECMA script reading state. When in this state everything upto to the next
+     * <code>%&gt;</code> is written to the output verbatim with three
+     * exceptions : ECMA slash star comments are handed over to handled by the
+     * {@link #PARSE_STATE_ECMA_COMMENT} state, quoted strings are handled in
+     * the {@link #PARSE_STATE_QUOTE} state and ECMA slash slash comments are
+     * handled in {@link #PARSE_STATE_ECMA_COMMENTL} state.
+     */
+    private static final byte PARSE_STATE_ECMA = 3;
+
+    /**
+     * ECMA script expression reading state. This state works exactly the same
+     * as the {@link #PARSE_STATE_ECMA} state with one exception: The whole
+     * code enclosed in the <code>&lt;%=</code> ... <code>%&gt;</code> tags
+     * is itself wrapped with a <code>out.write()</code> statement
+     * verbatim.
+     */
+    private static final byte PARSE_STATE_ECMA_EXPR = 4;
+
+    /**
+     * JSP comment reading state. When in this state everything upto the closing
+     * <code>--&gt;</code> tag is removed from the stream.
+     */
+    private static final byte PARSE_STATE_JSP_COMMENT = 5;
+
+    /**
+     * ECMA quoted string reading state. When in this state everything is
+     * written exactly as in the input stream upto the closing quote, which
+     * matches the opening quote.
+     */
+    private static final byte PARSE_STATE_QUOTE = 6;
+
+    /**
+     * Verbatim copy state. When in this state as many as verbatimChars
+     * characters are returned unchecked. As soon as this number of characters
+     * is returned, the last state is popped from the stack. This state is
+     * mainly used to (re-)inject static text into the output without further
+     * processing.
+     */
+    private static final byte PARSE_STATE_VERBATIM = 8;
+
+    /**
+     * ECMA Comment reading state. When in this state, an ECMA slash star
+     * comment is read (and completely returned).
+     */
+    private static final byte PARSE_STATE_ECMA_COMMENT = 9;
+
+    /**
+     * ECMA Comment reading state. When in this state, an ECMA slash slash
+     * comment is read (and completely returned).
+     */
+    private static final byte PARSE_STATE_ECMA_COMMENTL = 10;
+
+    /**
+     * To work with lookahead and character insertion, we use a PushbackReader.
+     */
+    private PushbackReader input;
+
+    /**
+     * Current parse state. This field contains one of the
+     * <code>PARSE_STATE</code> constants.
+     */
+    private byte state;
+
+    /**
+     * Stack of states. Whenever we enter a new state, the old state is pushed
+     * onto the stack. When a state is left, the previous one is popped from the
+     * stack.
+     *
+     * @see #pushState(byte)
+     * @see #popState()
+     * @see #state
+     */
+    private Stack<Byte> stateStack;
+
+    /**
+     * This value is set to true, if the parser is expected to insert a
+     * out.write() call into the input stream when in state
+     * {@link #PARSE_STATE_ESP}. When this field is true, it is not
+     * necessairily the case, that we are at the start of a real text line.
+     */
+    private boolean lineStart;
+
+    /**
+     * If characters are put into the pushback Stream that should be given back
+     * verbatim, this value is set to the number of such consecutive characters.
+     */
+    private int verbatimChars;
+
+    /**
+     * During String matching this is the character used for string quoting.
+     */
+    private char quoteChar;
+
+    /**
+     * Set to true if an escape character (\) has been encountered within a
+     * quoted string.
+     */
+    private boolean escape;
+
+    /**
+     * Whether the definition of the out variable has already been written or not.
+     * The initial value is <code>true</code> indicating it has still to be
+     * defined.
+     *
+     * @see #startWrite(String)
+     */
+    private boolean outUndefined = true;
+
+    /**
+     * Create an EspReader on top of the given <code>baseReader</code>. The
+     * constructor wraps the input reader with a <code>PushbackReader</code>,
+     * so that input stream modifications may be handled transparently by our
+     * {@link #doRead()} method.
+     */
+    public EspReader(Reader baseReader) {
+        super(baseReader);
+        this.input = new PushbackReader(baseReader, 100);
+        this.stateStack = new Stack<Byte>();
+        this.lineStart = true;
+        this.verbatimChars = -1;
+        this.quoteChar = 0;
+        this.escape = false;
+
+        // Start in ESP (template text) state
+        pushState(PARSE_STATE_ESP);
+    }
+
+    /**
+     * Check whether we may block at the next read() operation. We may be ready
+     * if and only if our input reader is ready. But this does not guarantee
+     * that we won't block, as due to filtering there may be more than one
+     * character needed from the input to return one.
+     *
+     * @return <code>true</code> if a character is available on the
+     *         <code>PushbackReader</code>.
+     * @throws IOException if the reader is not open
+     */
+    public boolean ready() throws IOException {
+        ensureOpen();
+        return input.ready();
+    }
+
+    /**
+     * Return the next filtered character. This need not be the next character
+     * of the input stream. It may be a character from the input reader, after
+     * having skipped filtered characters or it may be a character injected due
+     * to translation of template text to ECMA code.
+     *
+     * @return the next character after filtering or -1 at the end of the input
+     *         reader
+     * @throws IOException if the reader is not open
+     */
+    public int read() throws IOException {
+        ensureOpen();
+        return doRead();
+    }
+
+    /**
+     * Fill the given buffer with filtered or injected characters. This need not
+     * be the next characters of the input stream. It may be characters from the
+     * input reader, after having skipped filtered characters or it may be a
+     * characters injected due to translation of template text to ECMA code.
+     * This method is exactly the same as
+     * <code>read(cbuf, 0, cbuf.length)</code>.
+     *
+     * @param cbuf The character buffer to fill with (filtered) characters
+     * @return the number of characters filled in the buffer or -1 at the end of
+     *         the input reader.
+     * @throws IOException if the reader is not open
+     */
+    public int read(char[] cbuf) throws IOException {
+        return read(cbuf, 0, cbuf.length);
+    }
+
+    /**
+     * Fill the buffer from the offset with the number of characters given. This
+     * need not be the next characters of the input stream. It may be characters
+     * from the input reader, after having skipped filtered characters or it may
+     * be a characters injected due to translation of template text to ECMA
+     * code.
+     *
+     * @param cbuf The character buffer to fill with (filtered) characters
+     * @param off Offset from where to start in the buffer
+     * @param len The number of characters to fill into the buffer
+     * @return the number of characters filled in the buffer or -1 at the end of
+     *         the input reader.
+     * @throws IOException if the reader is not open
+     * @throws IndexOutOfBoundsException if len is negative, off is negative or
+     *             higher than the buffer length or off+len is negative or
+     *             beyond the buffer size.
+     */
+    public int read(char[] cbuf, int off, int len) throws java.io.IOException {
+        ensureOpen();
+
+        // Check lines (taken from InputStreamReader ;-)
+        if ((off < 0) || (off > cbuf.length) || (len < 0)
+            || ((off + len) > cbuf.length) || ((off + len) < 0)) {
+            throw new IndexOutOfBoundsException();
+        } else if (len == 0) {
+            return 0;
+        }
+
+        int i;
+        for (i = 0; i < len; i++, off++) {
+            int c = doRead();
+            if (c < 0) {
+                break;
+            }
+            cbuf[off] = (char) c;
+        }
+
+        // return EOF (-1) if none have been read, else return the number read
+        return (i == 0) ? -1 : i;
+    }
+
+    /**
+     * Skip the number of filtered characters. The skip method is the same as
+     * calling read() repeatedly for the given number of characters and throwing
+     * away the result. If the end of input reader is reached before having
+     * skipped the number of characters, the method returns the number
+     * characters skipped so far.
+     *
+     * @param n the number of (filtered) characters to skip
+     * @return the number of (filtered) characters actually skipped
+     * @throws IllegalArgumentException if n is negative
+     * @throws IOException if the reading the characters throws
+     */
+    public long skip(long n) throws IOException {
+        if (n < 0L) {
+            throw new IllegalArgumentException("skip value is negative");
+        }
+
+        long i = -1;
+        while (++i < n) {
+            if (doRead() < 0) {
+                break;
+            }
+        }
+        return i;
+    }
+
+    /**
+     * Close the EspReader.
+     */
+    public void close() throws java.io.IOException {
+        if (input != null) {
+            input.close();
+            input = null;
+        }
+
+        // I dont' know what happens ??
+        super.close();
+    }
+
+    /**
+     * Mark the present position in the stream. The <code>mark</code> for
+     * class <code>EspReader</code> always throws an throwable.
+     *
+     * @param readAheadLimit The number of characters to read ahead
+     * @exception IOException Always, since mark is not supported
+     */
+    public void mark(int readAheadLimit) throws IOException {
+        throw new IOException("mark() not supported");
+    }
+
+    /**
+     * Tell whether this stream supports the mark() operation, which it does
+     * not.
+     *
+     * @return false Always, since mark is not supported
+     */
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * Reset the stream. The <code>reset</code> method of
+     * <code>EspReader</code> always throws an throwable.
+     *
+     * @exception IOException Always, since reset is not supported
+     */
+    public void reset() throws IOException {
+        throw new IOException("reset() not supported");
+    }
+
+    /**
+     * Internal routine doing all the footwork of reading one character at a
+     * time from the <code>PushbackReader</code> and acting according to the
+     * current state.
+     * <p>
+     * This filter is implemented using a finite state machine using the states
+     * defined above with the <code>PARSE_STATE</code> constants. Each state
+     * may do a look ahead in certain situations to decide on further steps.
+     * Characters looked ahead may or may not be inserted back into the input
+     * stream depending on the concrete state.
+     *
+     * @return the next character from the input stream according to the current
+     *         state or -1 to indicate end of file.
+     * @throws IOException if the input <code>PushbackReader</code> throws it
+     */
+    private int doRead() throws IOException {
+
+        // we return out of the loop, if we find a character passing the filter
+        for (;;) {
+
+            // Get a character from the input, which may well have been
+            // injected using the unread() method
+            int c = input.read();
+
+            // catch EOF
+            if (c < 0) {
+
+                // if a template text line is still incomplete, inject
+                // proper line ending and continue until this has been returned
+                if (!lineStart && state == PARSE_STATE_ESP) {
+                    doVerbatim("\");"); // line ending injection
+                    lineStart = true; // mark the line having ended
+                    continue; // let's start read the injection
+                }
+
+                return c; // return the marker, we're done
+            }
+
+            // Do the finite state machine
+            switch (state) {
+
+                // NOTE :
+                // - continue means ignore current character, read next
+                // - break means return current character
+
+                // Template text state - text is wrapped in out.write()
+                case PARSE_STATE_ESP:
+
+                    // might start ECMA code/expr, ESP comment or JSP comment
+                    if (c == '<') {
+                        int c2 = input.read();
+                        int c3 = input.read();
+
+                        if (c2 == '%') {
+
+                            // ECMA or JSP comment
+
+                            if (c3 == '=') {
+
+                                // ECMA expression <%= ... %>
+                                pushState(PARSE_STATE_ECMA_EXPR);
+                                startWrite(null);
+                                if (!lineStart) {
+                                    doVerbatim("\");");
+                                }
+                                continue;
+
+                            } else if (c3 == '-') {
+
+                                // (Possible) JSP Comment <%-- ... --%>
+                                int c4 = input.read();
+                                if (c4 == '-') {
+                                    pushState(PARSE_STATE_JSP_COMMENT);
+                                    continue;
+                                }
+                                input.unread(c4);
+
+                            }
+
+                            // We only get here if we are sure about ECMA
+
+                            // ECMA code <% ... %>
+                            input.unread(c3);
+                            pushState(PARSE_STATE_ECMA);
+                            if (!lineStart) {
+                                doVerbatim("\");");
+                            }
+                            continue;
+
+                        }
+
+                        // Nothing special, push back read ahead
+                        input.unread(c3);
+                        input.unread(c2);
+
+                        // End of template text line
+                    } else if (c == '\r' || c == '\n') {
+                        String lineEnd; // will be injected
+
+                        // Check for real CRLF
+                        if (c == '\r') {
+                            int c2 = input.read();
+                            if (c2 != '\n') {
+                                input.unread(c2);
+                                lineEnd = "\\r";
+                            } else {
+                                lineEnd = "\\r\\n";
+                            }
+                        } else {
+                            lineEnd = "\\n";
+                        }
+
+                        // Only write line ending if not empty
+                        if (!lineStart) {
+                            doVerbatim("\");\n");
+                            doVerbatim(lineEnd);
+                            lineStart = true;
+
+                        } else { // if (lineEnd.length() > 1) {
+                            // no matter what line ending we have, make it LF
+                            doVerbatim("\");\n");
+                            doVerbatim(lineEnd);
+                            startWrite("\"");
+                        }
+
+                        continue;
+
+                        // template text is wrapped with double quotes, which
+                        // when occurring in the text must be escaped.
+                        // We also escape the escape character..
+                    } else if (c == '"' || c == '\\') {
+
+                        doVerbatim(String.valueOf((char) c));
+                        c = '\\';
+
+                    }
+
+                    // If in template text at the beginning of a line
+                    if (lineStart) {
+                        lineStart = false;
+                        startWrite("\"" + (char) c);
+                        continue;
+                    }
+
+                    break;
+
+                // Reading ECMA code or and ECMA expression
+                case PARSE_STATE_ECMA_EXPR:
+                case PARSE_STATE_ECMA:
+
+                    if (c == '%') {
+
+                        // might return to PARSE_STATE_ESP
+                        int c2 = input.read();
+                        if (c2 == '>') {
+
+                            // An expression is wrapped in out.write()
+                            if (popState() == PARSE_STATE_ECMA_EXPR) {
+                                doVerbatim(");");
+                            }
+
+                            // next ESP needs out.write(
+                            lineStart = true;
+
+                            continue;
+
+                        }
+
+                        // false alert, push back
+                        input.unread(c2);
+
+                    } else if (c == '/') {
+
+                        // might be ECMA Comment
+                        int c2 = input.read();
+                        if (c2 == '/') {
+                            // single line comment
+                            pushState(PARSE_STATE_ECMA_COMMENTL);
+                        } else if (c2 == '*') {
+                            // multiline comment
+                            pushState(PARSE_STATE_ECMA_COMMENT);
+                        }
+
+                        // false alert, push back
+                        input.unread(c2);
+
+                    } else if (c == '\'' || c == '"') {
+
+                        // an ECMA string
+                        escape = false; // start unescaped
+                        quoteChar = (char) c; // to recognize the end
+                        pushState(PARSE_STATE_QUOTE);
+
+                    }
+                    break;
+
+                // Reading a JSP comment, only returning line endings
+                case PARSE_STATE_JSP_COMMENT:
+
+                    // JSP comments end complexly with --%>
+                    if (c == '-') {
+                        int c2 = input.read();
+                        if (c2 == '-') {
+                            int c3 = input.read();
+                            if (c3 == '%') {
+                                int c4 = input.read();
+                                if (c4 == '>') {
+
+                                    // we really reached the end ...
+                                    popState();
+                                    continue;
+
+                                }
+                                input.unread(c4);
+                            }
+                            input.unread(c3);
+                        }
+                        input.unread(c2);
+
+                        // well, not definitely correct but reasonably accurate
+                        // ;-)
+                    } else if (c == '\r' || c == '\n') {
+
+                        // terminate an open template line
+                        if (!lineStart) {
+                            input.unread(c); // push back the character
+                            doVerbatim("\");"); // insert ");
+                            lineStart = true; // mark the line start
+                            continue; // Force read of the "
+                        }
+
+                        break;
+                    }
+
+                    // continue reading another character in the comment
+                    continue;
+
+                    // Read an ECMA string upto the ending quote character
+                case PARSE_STATE_QUOTE:
+
+                    // if unescaped quote character
+                    if (c == quoteChar && !escape) {
+                        popState();
+                    } else {
+                        // mark escape - only if not already escaped (bug 7079)
+                        escape = c == '\\' && !escape;
+                    }
+
+                    break;
+
+                // Return characters unfiltered
+                case PARSE_STATE_VERBATIM:
+
+                    // Go back to previous state if all characters read
+                    if (--verbatimChars < 0) {
+                        popState();
+                    }
+
+                    break;
+
+                // Return an ECMA multiline comment, ending with */
+                case PARSE_STATE_ECMA_COMMENT:
+
+                    // Might be the end of the comment
+                    if (c == '*') {
+                        int c2 = input.read();
+                        if (c2 == '/') {
+                            popState(); // back to previous
+                            doVerbatim("/"); // return slash verbatim
+                        } else {
+                            input.unread(c2);
+                        }
+                    }
+
+                    break;
+
+                // Return an ECMA single line comment, ending with end of line
+                case PARSE_STATE_ECMA_COMMENTL:
+
+                    // CRLF recognition
+                    if (c == '\r') {
+                        int c2 = input.read();
+                        if (c2 == '\n') {
+                            popState();
+                        }
+                        input.unread(c2);
+
+                        // LF only line end
+                    } else if (c == '\n') {
+                        popState();
+                    }
+
+                    break;
+
+                // What ???!!!
+                default:
+
+                    // we warn and go back to default state
+                    log.warn("doRead(): unknown state " + state);
+                    state = PARSE_STATE_ESP;
+
+                    break;
+
+            } // switch
+
+            // Exiting the switch normally we return the current character
+            return c;
+
+        } // for(;;)
+
+    }
+
+    /**
+     * Throw an IOException if the reader is not open
+     *
+     * @throws IOException if the reader is (already) closed
+     */
+    private void ensureOpen() throws IOException {
+        if (input == null) {
+            throw new IOException("Reader is closed");
+        }
+    }
+
+    /**
+     * Injects the call to write template text and checks whether the global
+     * <em>out</em> variable has also to be defined such that the writer is
+     * acquired on demand.
+     *
+     * @param startString Additional data to be injected as initial argument
+     *      to the <em>out.write</em> call written. If <code>null</code> just
+     *      the method call is injected.
+     *
+     * @throws IOException if the 'unreading' throws
+     */
+    private void startWrite(String startString) throws IOException {
+
+        // inject the out.write( part and the initial string
+        if (startString != null && startString.length() > 0) {
+            doVerbatim(startString);
+        }
+        doVerbatim("out.write(");
+
+        // if out is not set yet, we also acquire it now setting it
+        // globally
+        if (outUndefined) {
+            doVerbatim("out=response.writer;");
+            outUndefined = false;
+        }
+    }
+
+    /**
+     * Injects a string into the input stream, sets the number of characters to
+     * return verbatim and change state. The state change only happens if we are
+     * not in verbatim state already. Else the current string is simply
+     * prepended to the previous inhjection. This is simply a convenience method
+     * ;-)
+     *
+     * @param verbatimString The string to inject into the input stream
+     * @throws IOException if the 'unreading' throws
+     */
+    private void doVerbatim(String verbatimString) throws IOException {
+
+        // Push 'back' into PushbackReader
+        input.unread(verbatimString.toCharArray());
+
+        // Set the number of characters to return verbatim
+        verbatimChars += verbatimString.length();
+
+        // Change state if not already in verbatim state
+        if (state != PARSE_STATE_VERBATIM) {
+            pushState(PARSE_STATE_VERBATIM);
+        }
+    }
+
+    /**
+     * Push the current state on stack and set to <code>newState</code>. This
+     * new state is also returned.
+     *
+     * @param newState the new state to set
+     * @return the new state set according to <code>newState</code>
+     */
+    private byte pushState(byte newState) {
+        stateStack.push(state);
+        return state = newState;
+    }
+
+    /**
+     * Sets the current state to the state stored at the top of the stack. If
+     * the stack is empty prior to this call, the default template text state is
+     * set. The method returns the state prior to setting to the new state.
+     *
+     * @return the state prior to calling this method
+     */
+    private byte popState() {
+        byte oldState = state;
+        state = stateStack.isEmpty() ? PARSE_STATE_ESP : stateStack.pop();
+        return oldState;
+    }
+
+}
\ No newline at end of file

Propchange: incubator/sling/trunk/microsling/microsling-core/src/main/java/org/apache/sling/microsling/scripting/helpers/EspReader.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message