Author: radu
Date: Mon Aug 17 12:08:51 2015
New Revision: 1696254

URL: http://svn.apache.org/r1696254
Log:
SLING-4953 - Enhance the JavaScript API provided by the 
org.apache.sling.scripting.javascript bundle

* added a wrapper for Map objects so that Map keys are mapped as object 
properties for the JavaScript object
representing the map
* added a 'properties' property for Javascript objects representing resources

Added:
    
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMap.java
    
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/
    
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMapTest.java
Modified:
    
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
    
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableResource.java
    
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/wrapper/ScriptableResourceTest.java

Modified: 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java?rev=1696254&r1=1696253&r2=1696254&view=diff
==============================================================================
--- 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
 (original)
+++ 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngineFactory.java
 Mon Aug 17 12:08:51 2015
@@ -42,6 +42,7 @@ import org.apache.sling.scripting.javasc
 import org.apache.sling.scripting.javascript.helper.SlingWrapFactory;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableCalendar;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableItemMap;
+import org.apache.sling.scripting.javascript.wrapper.ScriptableMap;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableNode;
 import org.apache.sling.scripting.javascript.wrapper.ScriptablePrintWriter;
 import org.apache.sling.scripting.javascript.wrapper.ScriptableProperty;
@@ -99,7 +100,7 @@ public class RhinoJavaScriptEngineFactor
             ScriptableResource.class, ScriptableNode.class,
             ScriptableProperty.class, ScriptableItemMap.class,
             ScriptablePrintWriter.class, ScriptableVersionHistory.class,
-            ScriptableVersion.class, ScriptableCalendar.class
+            ScriptableVersion.class, ScriptableCalendar.class, 
ScriptableMap.class
     };
 
     /**

Added: 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMap.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMap.java?rev=1696254&view=auto
==============================================================================
--- 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMap.java
 (added)
+++ 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMap.java
 Mon Aug 17 12:08:51 2015
@@ -0,0 +1,88 @@
+/*
+ * 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.javascript.wrapper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.sling.scripting.javascript.SlingWrapper;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.Undefined;
+
+/**
+ * The {@code ScriptableMap} wrapper provides easier access to a map's values 
by setting the map's keys as properties to the JavaScript
+ * object representing the {@link Map}.
+ */
+public class ScriptableMap extends ScriptableBase implements SlingWrapper {
+
+    public static final String CLASSNAME = "Map";
+    private static final Class<?> [] WRAPPED_CLASSES = { Map.class };
+
+    private Map<String, Object> map = new HashMap<String, Object>();
+
+    public void jsConstructor(Object map) {
+        this.map = (Map) map;
+    }
+
+    @Override
+    public Object get(String name, Scriptable start) {
+        final Object fromSuperClass = super.get(name, start);
+        if (fromSuperClass != Scriptable.NOT_FOUND) {
+            return fromSuperClass;
+        }
+
+        if (map == null) {
+            return Undefined.instance;
+        }
+
+        Object result = map.get(name);
+        if (result == null) {
+            result = getNative(name, start);
+        }
+        return result;
+    }
+
+    @Override
+    public Object getDefaultValue(Class<?> typeHint) {
+        return map;
+    }
+
+    @Override
+    protected Object getWrappedObject() {
+        return map;
+    }
+
+    @Override
+    protected Class<?> getStaticType() {
+        return Map.class;
+    }
+
+    @Override
+    public String getClassName() {
+        return CLASSNAME;
+    }
+
+    @Override
+    public Class<?>[] getWrappedClasses() {
+        return WRAPPED_CLASSES;
+    }
+
+    @Override
+    public Object unwrap() {
+        return map;
+    }
+}

Modified: 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableResource.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableResource.java?rev=1696254&r1=1696253&r2=1696254&view=diff
==============================================================================
--- 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableResource.java
 (original)
+++ 
sling/trunk/bundles/scripting/javascript/src/main/java/org/apache/sling/scripting/javascript/wrapper/ScriptableResource.java
 Mon Aug 17 12:08:51 2015
@@ -18,6 +18,7 @@ package org.apache.sling.scripting.javas
 
 import org.apache.commons.collections.IteratorUtils;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.scripting.javascript.SlingWrapper;
 import org.mozilla.javascript.Context;
 import org.mozilla.javascript.Function;
@@ -51,6 +52,7 @@ import org.slf4j.LoggerFactory;
  * <li>[Resource[]] getChildren()</li>
  * <li>[Resource[]] listChildren()</li>
  * <li>[Boolean] isResourceType(String)</li>
+ * <li>[Object] properties</li>
  * </ul>
  */
 public class ScriptableResource extends ScriptableObject implements
@@ -63,6 +65,7 @@ public class ScriptableResource extends
     private static final Class<?>[] WRAPPED_CLASSES = { Resource.class };
 
     private Resource resource;
+    private ValueMap properties;
 
     public ScriptableResource() {
     }
@@ -307,6 +310,13 @@ public class ScriptableResource extends
         return Undefined.instance;
     }
 
+    public Object jsGet_properties() {
+        if (properties == null) {
+            properties = resource.adaptTo(ValueMap.class);
+        }
+        return properties;
+    }
+
     // --------- ScriptableObject API
 
     @Override

Added: 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMapTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMapTest.java?rev=1696254&view=auto
==============================================================================
--- 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMapTest.java
 (added)
+++ 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/javascript/wrapper/ScriptableMapTest.java
 Mon Aug 17 12:08:51 2015
@@ -0,0 +1,63 @@
+/*
+ * 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.javascript.wrapper;
+
+import java.util.HashMap;
+import javax.script.ScriptException;
+
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.scripting.RepositoryScriptingTestBase;
+import org.apache.sling.scripting.javascript.internal.ScriptEngineHelper;
+import org.junit.After;
+import org.junit.Before;
+
+public class ScriptableMapTest extends RepositoryScriptingTestBase {
+
+    private ValueMap valueMap;
+    private ScriptEngineHelper.Data data;
+
+    @Before
+    public void setUp() throws Exception {
+        super.setUp();
+        valueMap = new ValueMapDecorator(new HashMap<String, Object>() {{
+            put("a", "a");
+            put("b", 1);
+        }});
+        data = new ScriptEngineHelper.Data();
+        data.put("properties", valueMap);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        valueMap.clear();
+        data.clear();
+        super.tearDown();
+    }
+
+    public void testPropertyAccess() throws ScriptException {
+        assertEquals("a", script.eval("properties['a']", data));
+        assertEquals("a", script.eval("properties.a", data));
+        assertEquals(1, script.eval("properties['b']", data));
+        assertEquals(1, script.eval("properties.b", data));
+        assertEquals(null, script.eval("properties['c']", data));
+    }
+
+    public void testJavaMethods() throws ScriptException {
+        assertEquals(2, script.eval("properties.size()", data));
+    }
+}
\ No newline at end of file

Modified: 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/wrapper/ScriptableResourceTest.java
URL: 
http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/wrapper/ScriptableResourceTest.java?rev=1696254&r1=1696253&r2=1696254&view=diff
==============================================================================
--- 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/wrapper/ScriptableResourceTest.java
 (original)
+++ 
sling/trunk/bundles/scripting/javascript/src/test/java/org/apache/sling/scripting/wrapper/ScriptableResourceTest.java
 Mon Aug 17 12:08:51 2015
@@ -19,25 +19,37 @@
 package org.apache.sling.scripting.wrapper;
 
 import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
-
+import java.util.Map;
 import javax.jcr.Item;
 import javax.jcr.NamespaceException;
 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 org.apache.jackrabbit.JcrConstants;
 import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.commons.testing.sling.MockResourceResolver;
 import org.apache.sling.jcr.resource.JcrResourceConstants;
 import org.apache.sling.scripting.RepositoryScriptingTestBase;
 import org.apache.sling.scripting.javascript.internal.ScriptEngineHelper;
 import org.mozilla.javascript.Wrapper;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 public class ScriptableResourceTest extends RepositoryScriptingTestBase {
 
@@ -49,6 +61,8 @@ public class ScriptableResourceTest exte
 
     private static final String RESOURCE_SUPER_TYPE = 
"testWrappedResourceSuperType";
 
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(ScriptableResourceTest.class);
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -215,6 +229,17 @@ public class ScriptableResourceTest exte
         assertEquals(true, 
script.eval("resource.adaptTo(Packages.java.util.Date) == undefined", data));
     }
 
+    public void testProperties() throws Exception {
+        final ScriptEngineHelper.Data data = new ScriptEngineHelper.Data();
+        Calendar date = new GregorianCalendar();
+        node.setProperty(JcrConstants.JCR_LASTMODIFIED, date);
+        node.setProperty("test", "testProperties");
+        node.getSession().save();
+        data.put("resource", new TestResource(node));
+        assertEquals(date.getTimeInMillis(), 
script.eval("(resource.properties['jcr:lastModified']).getTimeInMillis()", 
data));
+        assertEquals("testProperties", script.eval("resource.properties.test", 
data));
+    }
+
     private void assertEquals(Node expected, Object actual) {
         while (actual instanceof Wrapper) {
             actual = ((Wrapper) actual).unwrap();
@@ -271,6 +296,75 @@ public class ScriptableResourceTest exte
         public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
             if (type == Node.class || type == Item.class) {
                 return (AdapterType) node;
+            } else if (type == ValueMap.class) {
+                try {
+
+                    PropertyIterator iterator = node.getProperties();
+                    Map<String, Object> properties = new HashMap<String, 
Object>();
+                    while (iterator.hasNext()) {
+                        Property prop = iterator.nextProperty();
+                        if (prop.isMultiple()) {
+                            Value[] values = prop.getValues();
+                            Object[] array = new Object[values.length];
+                            int index = 0;
+                            for (Value value : values) {
+                                switch (value.getType()) {
+                                    case PropertyType.BINARY:
+                                        array[index++] = value.getBinary();
+                                        break;
+                                    case PropertyType.BOOLEAN:
+                                        array[index++] = value.getBoolean();
+                                        break;
+                                    case PropertyType.DATE:
+                                        array[index++] = value.getDate();
+                                        break;
+                                    case PropertyType.DECIMAL:
+                                        array[index++] = value.getDecimal();
+                                        break;
+                                    case PropertyType.DOUBLE:
+                                        array[index++] = value.getDouble();
+                                        break;
+                                    case PropertyType.LONG:
+                                        array[index++] = value.getLong();
+                                        break;
+                                    default:
+                                        array[index++] = value.getString();
+                                        break;
+                                }
+                            }
+                            properties.put(prop.getName(), array);
+
+                        } else {
+                            Value value = prop.getValue();
+                            switch (value.getType()) {
+                                case PropertyType.BINARY:
+                                    properties.put(prop.getName(), 
value.getBinary());
+                                    break;
+                                case PropertyType.BOOLEAN:
+                                    properties.put(prop.getName(), 
value.getBoolean());
+                                    break;
+                                case PropertyType.DATE:
+                                    properties.put(prop.getName(), 
value.getDate());
+                                    break;
+                                case PropertyType.DECIMAL:
+                                    properties.put(prop.getName(), 
value.getDecimal());
+                                    break;
+                                case PropertyType.DOUBLE:
+                                    properties.put(prop.getName(), 
value.getDouble());
+                                    break;
+                                case PropertyType.LONG:
+                                    properties.put(prop.getName(), 
value.getLong());
+                                    break;
+                                default:
+                                    properties.put(prop.getName(), 
value.getString());
+                                    break;
+                            }
+                        }
+                    }
+                    return ((AdapterType) (new ValueMapDecorator(properties)));
+                } catch (RepositoryException e) {
+                    LOGGER.error("Unable to adapt resource " + getPath() + " 
to a ValueMap.", e);
+                }
             }
 
             return null;


Reply via email to