/* * 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 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; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Undefined; import org.mozilla.javascript.Wrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Resource in JavaScript has following signature: * <ul> * <li>[String] getName()</li> * <li>[String] name</li> * <li>[Object] getResourceMetadata()</li> * <li>[Object] resourceMetadata</li> * <li>[String] getPath()</li> * <li>[String] path</li> * <li>[String] getResourceType()</li> * <li>[String] resourceType</li> * <li>[String] getResourceSuperType()</li> * <li>[String] resourceSuperType</li> * <li>[Resource] getParent()</li> * <li>[Resource] parent</li> * <li>[ResourceResolver] getResourceResolver()</li> * <li>[ResourceResolver] resourceResolver</li> * <li>[Resource] getChild(String)</li> * <li>[Resource[]] getChildren()</li> * <li>[Resource[]] listChildren()</li> * <li>[Boolean] isResourceType(String)</li> * <li>[Object] properties</li> * </ul> */ public class ScriptableResource extends ScriptableObject implements SlingWrapper { private static final Logger LOGGER = LoggerFactory.getLogger(ScriptableResource.class); public static final String CLASSNAME = "Resource"; private static final Class<?>[] WRAPPED_CLASSES = { Resource.class }; private Resource resource; private ValueMap properties; public ScriptableResource() { } public ScriptableResource(Resource resource) { this.resource = resource; } public void jsConstructor(Object res) { this.resource = (Resource) res; } /* * Maps getName() method as name property. */ public String jsGet_name() { return this.jsFunction_getName(); } /* * Maps getName() method as getName() method. */ public String jsFunction_getName() { return resource.getName(); } /* * Maps getPath() method as path property. */ public String jsGet_path() { return this.jsFunction_getPath(); } /* * Maps getPath() method as getPath() method. */ public String jsFunction_getPath() { return resource.getPath(); } /** * Maps getResourceType() to type property. This property is deprecated * since it does not correctly map the getResourceType() method name to a * property. * * @deprecated since 2.1.0 because it maps the method name incorrectly. * @return the resource type */ @Deprecated public String jsGet_type() { return this.jsFunction_getResourceType(); } /* * Maps getResourceType() to resourceType property. */ public String jsGet_resourceType() { return this.jsFunction_getResourceType(); } /* * Maps getResourceType() to the getResourceType() method. */ public String jsFunction_getResourceType() { return resource.getResourceType(); } public NativeArray jsFunction_listChildren() { return jsFunction_getChildren(); } public NativeArray jsFunction_getChildren() { return new NativeArray(IteratorUtils.toArray(resource.listChildren())); } /* * Maps getParent() method as parent property. */ public Object jsGet_parent() { return this.jsFunction_getParent(); } /* * Maps getParent() method as getParent() method. */ public Object jsFunction_getParent() { return resource.getParent(); } /* * Maps getResourceSuperType() to resourceSuperType property. */ public String jsGet_resourceSuperType() { return this.jsFunction_getResourceSuperType(); } /* * Maps getResourceSuperType() to the getResourceSuperType() method. */ public String jsFunction_getResourceSuperType() { return resource.getResourceSuperType(); } public boolean jsFunction_isResourceType(String type) { return resource.isResourceType(type); } /** * Maps getResourceMetadata() to meta property. This property is deprecated * since it does not correctly map the getResourceType() method name to a * property. * * @deprecated since 2.1.0 because it maps the method name incorrectly. * @return the resource metadata */ @Deprecated public Object jsGet_meta() { return jsFunction_getResourceMetadata(); } /* * Maps getResourceMetadata() to resourceMetadata property. */ public Object jsGet_resourceMetadata() { return jsFunction_getResourceMetadata(); } /** * Maps getResourceMetadata() to getMetadata() method. This method is * deprecated since it has the wrong name to support the * getResourceMetadata() method. * * @deprecated since 2.1.0 because the method is named incorrectly. * @return the resource metadata */ @Deprecated public Object jsFunction_getMetadata() { return jsFunction_getResourceMetadata(); } /* * Maps getResourceMetadata() to getResourceMetadata method. */ public Object jsFunction_getResourceMetadata() { return toJS(resource.getResourceMetadata()); } /* * Maps getResourceResolver() to resourceResolver property. */ public Object jsFunction_getResourceResolver() { return toJS(resource.getResourceResolver()); } /* * Maps getResourceResolver() to getResourceResolver method. */ public Object jsGet_resourceResolver() { return jsFunction_getResourceResolver(); } /** * Maps getChild(String childPath) to getChild method. * * @param childPath the child path * @return the child resource if one exists at {@code childPath} or {@code null} */ public Object jsFunction_getChild(String childPath) { return resource.getChild(childPath); } /* * Helper method to easily retrieve the default adapted object of the * resource. In case of Object Content Mapping support, this method will * return the correctly mapped content object for this resource. * * Calling this method is equivalent to calling the adaptTo method with the * argument "java.lang.Object". */ public Object jsFunction_getObject() { return toJS(resource.adaptTo(Object.class)); } /** * Implements the adaptTo() method for JavaScript scripts. This method takes * either a java.lang.Class object or a String containing the fully * qualified name of the class to adapt to. * <p> * Supporting String as an argument to this method allows for much easier * use in JavaScript since instead of for example writing * <i>"javax.jcr.Node"</i> instead of the much clumsier * <i>Packages.javax.jcr.Node</i>. * * @param cx The current Rhino context * @param thisObj The ScriptableResource object in which the method is * called. * @param args The argument vector. Only the first argument is used which is * expected to be a Class object or a String. If no argument is * supplied or it has the wrong type, this method just returns * <code>null</code>. * @param funObj The object representing the JavaScript adaptTo function. * @return The object to which the resource adapts or <code>null</code> if * the resource does not adapt to the required type or if the * argument is of the wrong type or missing. */ public static Object jsFunction_adaptTo(Context cx, Scriptable thisObj, Object[] args, Function funObj) { // get and unwrap the argument Object arg = (args.length > 0) ? args[0] : null; while (arg instanceof Wrapper) { arg = ((Wrapper) arg).unwrap(); } // try to get the Class object for the argument Class<?> adapter = null; if (arg instanceof Class<?>) { adapter = (Class<?>) arg; } else if (arg != null && arg != Undefined.instance) { // try loading the class from the String String className = ScriptRuntime.toString(arg); try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = thisObj.getClass().getClassLoader(); } adapter = loader.loadClass(className); } catch (Exception e) { LOGGER.error("Unable to adapt object.", e); } } if (adapter != null) { ScriptableResource sr = (ScriptableResource) thisObj; return sr.toJS(sr.resource.adaptTo(adapter)); } return Undefined.instance; } public Object jsGet_properties() { if (properties == null) { properties = resource.adaptTo(ValueMap.class); } return properties; } // --------- ScriptableObject API @Override public String getClassName() { return CLASSNAME; } @SuppressWarnings("unchecked") @Override public Object getDefaultValue(Class typeHint) { if (resource != null) { return resource.getPath(); } return String.valueOf((Object) null); } public void setResource(Resource entry) { this.resource = entry; } // ---------- SlingWrapper API public Class<?>[] getWrappedClasses() { return WRAPPED_CLASSES; } // ---------- Wrapper interface -------------------------------------------- // returns the wrapped resource public Object unwrap() { return resource; } // ---------- Internal helper ---------------------------------------------- private Object toJS(Object javaObject) { if (javaObject == null) { return Undefined.instance; } return ScriptRuntime.toObject(this, javaObject); } }