You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by aw...@apache.org on 2009/02/06 01:31:20 UTC
svn commit: r741354 [1/2] - in /incubator/shindig/trunk: ./ java/common/
java/common/src/main/java/org/apache/shindig/config/
java/common/src/main/java/org/apache/shindig/expressions/
java/common/src/test/java/org/apache/shindig/config/ java/common/src...
Author: awiner
Date: Fri Feb 6 00:31:19 2009
New Revision: 741354
URL: http://svn.apache.org/viewvc?rev=741354&view=rev
Log:
SHINDIG-902: Support full expression syntax, not just "."
- Add JUEL, and use its implementation of the javax.el API.
- As there's no support for escaping "." in property names, switch from
${foo.some\.property} to ${foo['some.property']}
- Since there's no support for ['...'] at the top-level, switch from
${some\.property.foo} to ${Cur['some.property'].foo}
Benchmarks show this implementation is ca. 10% slower than the hand-coded version for concatenation and property access.
Added:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigELResolver.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/JsonELResolver.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/RootELResolver.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetELResolver.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetELResolverTest.java
Removed:
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigExpressionContext.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/ElException.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expression.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/ExpressionContext.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/ContainerConfigExpressionContextTest.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetExpressionContext.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetExpressionContextTest.java
Modified:
incubator/shindig/trunk/java/common/pom.xml
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/AbstractContainerConfig.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/DynamicConfigProperty.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java
incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expressions.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java
incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/expressions/ExpressionsTest.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java
incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/PersonHandlerTest.java
incubator/shindig/trunk/pom.xml
Modified: incubator/shindig/trunk/java/common/pom.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/pom.xml?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/pom.xml (original)
+++ incubator/shindig/trunk/java/common/pom.xml Fri Feb 6 00:31:19 2009
@@ -108,5 +108,9 @@
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
+ <dependency>
+ <groupId>de.odysseus.juel</groupId>
+ <artifactId>juel</artifactId>
+ </dependency>
</dependencies>
</project>
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/AbstractContainerConfig.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/AbstractContainerConfig.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/AbstractContainerConfig.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/AbstractContainerConfig.java Fri Feb 6 00:31:19 2009
@@ -55,7 +55,9 @@
public List<Object> getList(String container, String property) {
Object value = getProperty(container, property);
if (value instanceof List) {
- return (List<Object>) value;
+ @SuppressWarnings("unchecked")
+ List<Object> listValue = (List<Object>) value;
+ return listValue;
}
return Collections.emptyList();
}
@@ -63,7 +65,9 @@
public Map<String, Object> getMap(String container, String property) {
Object value = getProperty(container, property);
if (value instanceof Map) {
- return (Map<String, Object>) value;
+ @SuppressWarnings("unchecked")
+ Map<String, Object> mapValue = (Map<String, Object>) value;
+ return mapValue;
}
return Collections.emptyMap();
}
Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigELResolver.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigELResolver.java?rev=741354&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigELResolver.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/ContainerConfigELResolver.java Fri Feb 6 00:31:19 2009
@@ -0,0 +1,131 @@
+/*
+ * 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.shindig.config;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.PropertyNotWritableException;
+
+/**
+ * ELResolver that handles adds support for:
+ * - the "Cur" property, for explicit reference to the current container config
+ * - the "parent" property, for explicit and recursive reference to config parents
+ * - implicit reference to top-level properties in the current container config
+ * or inside any parents
+ */
+class ContainerConfigELResolver extends ELResolver {
+ /** Key for the current container. */
+ public static final String CURRENT_CONFIG_KEY = "Cur";
+
+ private final ContainerConfig config;
+ private final String currentContainer;
+
+ public ContainerConfigELResolver(ContainerConfig config, String currentContainer) {
+ this.config = config;
+ this.currentContainer = currentContainer;
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ if ((base == null) || (base instanceof ContainerReference)) {
+ return String.class;
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
+ Object base) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if ((base == null) || (base instanceof ContainerReference)) {
+ context.setPropertyResolved(true);
+ Object value = getValue(context, base, property);
+ return (value == null) ? null : value.getClass();
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ // Handle all requests off the base, and anything that is a reference to
+ // a container
+ String container = null;
+ if (base == null) {
+ container = currentContainer;
+ } else if (base instanceof ContainerReference) {
+ container = ((ContainerReference) base).containerName;
+ } else {
+ // Not ours - return without setPropertyResolved(true)
+ return null;
+ }
+
+ context.setPropertyResolved(true);
+ if (JsonContainerConfig.PARENT_KEY.equals(property)) {
+ // "parent": find the parent of the base, and return a ContainerReference
+ String parent = config.getString(container, JsonContainerConfig.PARENT_KEY);
+ if (parent == null) {
+ return null;
+ } else {
+ ContainerReference reference = new ContainerReference();
+ reference.containerName = parent;
+ return reference;
+ }
+ } else if (CURRENT_CONFIG_KEY.equals(property) && base == null) {
+ // "Cur": return a reference to the current container
+ ContainerReference reference = new ContainerReference();
+ reference.containerName = currentContainer;
+ return reference;
+ } else {
+ // Referring to a property of an existing container
+ return config.getProperty(container, property.toString());
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if ((base == null) || (base instanceof ContainerReference)) {
+ context.setPropertyResolved(true);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ // No support for mutating container configs
+ if ((base == null) || (base instanceof ContainerReference)) {
+ throw new PropertyNotWritableException();
+ }
+ }
+
+ /** A reference to the container, for later EL evaluation */
+ private static class ContainerReference {
+ public String containerName;
+ }
+}
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/DynamicConfigProperty.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/DynamicConfigProperty.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/DynamicConfigProperty.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/DynamicConfigProperty.java Fri Feb 6 00:31:19 2009
@@ -18,11 +18,15 @@
*/
package org.apache.shindig.config;
-import org.apache.shindig.expressions.ElException;
-import org.apache.shindig.expressions.Expression;
-import org.apache.shindig.expressions.ExpressionContext;
import org.apache.shindig.expressions.Expressions;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ValueExpression;
+
/**
* String property that can be interpreted using a container context.
@@ -30,25 +34,21 @@
* Implements CharSequence strictly as a marker. Only toString is supported.
*/
public class DynamicConfigProperty implements CharSequence {
- private final ExpressionContext context;
- private final Expression<String> expression;
+ private static final Logger logger = Logger.getLogger(DynamicConfigProperty.class.getName());
+ private final ELContext context;
+ private final ValueExpression expression;
- public DynamicConfigProperty(String value, ExpressionContext context) {
+ public DynamicConfigProperty(String value, Expressions expressions, ELContext context) {
this.context = context;
- Expression<String> expression = null;
- try {
- expression = Expressions.parse(value, String.class);
- } catch (ElException e) {
- expression = new Expressions.ConstantExpression<String>(value);
- }
- this.expression = expression;
+ this.expression = expressions.parse(value, String.class);
}
@Override
public String toString() {
try {
- return expression.evaluate(context);
- } catch (ElException e) {
+ return (String) expression.getValue(context);
+ } catch (ELException e) {
+ logger.log(Level.WARNING, "Evaluation of " + expression.getExpressionString() + " failed", e);
return "";
}
}
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/config/JsonContainerConfig.java Fri Feb 6 00:31:19 2009
@@ -19,19 +19,10 @@
package org.apache.shindig.config;
+import org.apache.commons.lang.StringUtils;
import org.apache.shindig.common.JsonSerializer;
import org.apache.shindig.common.util.ResourceLoader;
-import org.apache.shindig.expressions.ElException;
-import org.apache.shindig.expressions.Expression;
-import org.apache.shindig.expressions.ExpressionContext;
import org.apache.shindig.expressions.Expressions;
-
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import com.google.inject.name.Named;
-
-import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -47,6 +38,15 @@
import java.util.Map;
import java.util.logging.Logger;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ValueExpression;
+
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+import com.google.inject.name.Named;
+
/**
* Represents a container configuration using JSON notation.
*
@@ -67,18 +67,27 @@
public static final String CONTAINER_KEY = "gadgets.container";
private final Map<String, Map<String, Object>> config;
+ private final Expressions expressions;
/**
- * Creates a new, empty configuration.
- * @param containers
+ * Creates a new configuration from files.
* @throws ContainerConfigException
*/
@Inject
- public JsonContainerConfig(@Named("shindig.containers.default") String containers)
+ public JsonContainerConfig(@Named("shindig.containers.default") String containers, Expressions expressions)
throws ContainerConfigException {
+ this.expressions = expressions;
config = createContainers(loadContainers(containers));
}
+ /**
+ * Creates a new configuration from a JSON Object,Êfor use in testing.
+ */
+ public JsonContainerConfig(JSONObject json, Expressions expressions) {
+ this.expressions = expressions;
+ config = createContainers(json);
+ }
+
@Override
public Collection<String> getContainers() {
return Collections.unmodifiableSet(config.keySet());
@@ -94,9 +103,9 @@
if (property.startsWith("${")) {
// An expression!
try {
- Expression<String> expression = Expressions.parse(property, String.class);
- return expression.evaluate(createExpressionContext(container));
- } catch (ElException e) {
+ ValueExpression expression = expressions.parse(property, Object.class);
+ return expression.getValue(createExpressionContext(container));
+ } catch (ELException e) {
return null;
}
}
@@ -114,8 +123,8 @@
private Map<String, Map<String, Object>> createContainers(JSONObject json) {
Map<String, Map<String, Object>> map = Maps.newHashMap();
for (String container : JSONObject.getNames(json)) {
- ExpressionContext context = createExpressionContext(container);
- map.put(container, jsonToMap(json.optJSONObject(container), context));
+ ELContext context = createExpressionContext(container);
+ map.put(container, jsonToMap(json.optJSONObject(container), expressions, context));
}
return map;
@@ -124,35 +133,35 @@
/**
* Protected to allow overriding.
*/
- protected ExpressionContext createExpressionContext(String container) {
- return new ContainerConfigExpressionContext(container, this);
+ protected ELContext createExpressionContext(String container) {
+ return expressions.newELContext(new ContainerConfigELResolver(this, container));
}
/**
* Convert a JSON value to a configuration value.
*/
- private static Object jsonToConfig(Object json, ExpressionContext context) {
+ private static Object jsonToConfig(Object json, Expressions expressions, ELContext context) {
if (json instanceof CharSequence) {
- return new DynamicConfigProperty(json.toString(), context);
+ return new DynamicConfigProperty(json.toString(), expressions, context);
} else if (json instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) json;
List<Object> values = new ArrayList<Object>(jsonArray.length());
for (int i = 0, j = jsonArray.length(); i < j; ++i) {
- values.add(jsonToConfig(jsonArray.opt(i), context));
+ values.add(jsonToConfig(jsonArray.opt(i), expressions, context));
}
return Collections.unmodifiableList(values);
} else if (json instanceof JSONObject) {
- return jsonToMap((JSONObject) json, context);
+ return jsonToMap((JSONObject) json, expressions, context);
}
// A (boxed) primitive.
return json;
}
- private static Map<String, Object> jsonToMap(JSONObject json, ExpressionContext context) {
+ private static Map<String, Object> jsonToMap(JSONObject json, Expressions expressions, ELContext context) {
Map<String, Object> values = new HashMap<String, Object>(json.length(), 1);
for (String key : JSONObject.getNames(json)) {
- values.put(key, jsonToConfig(json.opt(key), context));
+ values.put(key, jsonToConfig(json.opt(key), expressions, context));
}
return Collections.unmodifiableMap(values);
}
Modified: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expressions.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expressions.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expressions.java (original)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/Expressions.java Fri Feb 6 00:31:19 2009
@@ -18,265 +18,192 @@
*/
package org.apache.shindig.expressions;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
import org.json.JSONArray;
-import org.json.JSONObject;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
+import java.util.Properties;
import java.util.StringTokenizer;
+import javax.el.ArrayELResolver;
+import javax.el.CompositeELResolver;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.ExpressionFactory;
+import javax.el.ListELResolver;
+import javax.el.MapELResolver;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+
+import com.google.inject.Inject;
+import com.google.inject.Singleton;
+
+import de.odysseus.el.ExpressionFactoryImpl;
+import de.odysseus.el.util.SimpleContext;
+
/**
- * Implementation of the small subset of JSP EL that OpenSocial supports:
- * - String concatenation
- * - The "." operator
- * - Limited type coercion
+ * A facade to the expressions functionality.
*/
+@Singleton
public class Expressions {
- /** Constant for the start of an expression */
- private static final String EXPRESSION_START = "${";
- /** Constant for the end of an expression */
- private static final char EXPRESSION_END = '}';
+ private static final Expressions sharedInstance = new Expressions();
+
+ private final ExpressionFactory factory;
+ private final ELContext parseContext;
+ private final ELResolver defaultELResolver;
/**
- * Parse a string into an Expression object.
- * @param <T> type of the expression
- * @param text the text in the expression
- * @param type type of object the expression will return
- * @throws ElException if errors are found in the expression while parsing.
- * Unterminated expressions, empty expressions, and constant values that
- * cannot be properly coerced will result in exceptions.
+ * Return a shared instance.
+ * TODO: inject Expressions into the gadget spec code and get rid of
+ * this singleton.
*/
- public static <T> Expression<T> parse(String text, Class<T> type) throws ElException {
- Preconditions.checkNotNull(text);
- Preconditions.checkNotNull(type);
-
- // Check for constant text (no EL), or one big EL expression
- int nextExpressionIndex = nextExpression(text, 0);
- if (nextExpressionIndex < 0) {
- T value = coerce(text, type);
- return new ConstantExpression<T>(value);
- } else if (nextExpressionIndex == 0 && endOfExpression(text, 2) == text.length() - 1) {
- return parseEL(text, 2, text.length() - 1, type);
- }
-
- // String concatenation case; find each expression
- List<Expression<String>> expressions = Lists.newArrayList();
- int start = 0;
- while (nextExpressionIndex >= 0) {
- // Add a constant expression if needed
- if (start < nextExpressionIndex) {
- String constantText = text.substring(start, nextExpressionIndex);
- expressions.add(new ConstantExpression<String>(constantText));
- }
-
- int endOfExpression = endOfExpression(text, nextExpressionIndex + 2);
- if (endOfExpression < 0) {
- throw new ElException("Unterminated expression in " + text);
- }
-
- expressions.add(parseEL(text, nextExpressionIndex + 2, endOfExpression, String.class));
- start = endOfExpression + 1;
- nextExpressionIndex = nextExpression(text, start);
- }
+ public static Expressions sharedInstance() {
+ return sharedInstance;
+ }
+
+ @Inject
+ public Expressions() {
+ factory = newExpressionFactory();
+ // Stub context with no FunctionMapper, used only to parse expressions
+ parseContext = new SimpleContext();
+ defaultELResolver = createDefaultELResolver();
+ }
- if (start < text.length()) {
- String constantText = text.substring(start);
- expressions.add(new ConstantExpression<String>(constantText));
+ /**
+ * Creates an ELContext.
+ * @param customResolvers resolvers to be added to the chain
+ */
+ public ELContext newELContext(ELResolver... customResolvers) {
+ CompositeELResolver composite = new CompositeELResolver();
+ for (ELResolver customResolver : customResolvers) {
+ composite.add(customResolver);
}
- return new ConcatExpression<T>(expressions, type);
+ composite.add(defaultELResolver);
+ return new SimpleContext(composite);
}
/**
- * Parses an EL expression within a string.
- * @throws ElException
+ * Parse a value expression.
+ * @param expression the string expression. This may be a literal
+ * without any expressions.
+ * @param type the desired coercion type.
+ * @return a ValueExpression corresponding to the expression
*/
- private static <T> Expression<T> parseEL(final String text, int from, int to,
- final Class<T> type) throws ElException {
- if (from == to) {
- throw new ElException("Empty expression in " + text);
- }
-
- // TODO: the spec only describes support for "a.b", not "a.b.c.d". Update
- // the spec, or limit this function?
- final List<String> segments = splitSegments(text.substring(from, to));
- return new Expression<T>() {
-
- public T evaluate(ExpressionContext context) throws ElException {
- Object value = context.getVariable(segments.get(0));
- for (int i = 1; i < segments.size(); i++) {
- if (value == null) {
- throw new ElException("Could not find property \"" + segments.get(i - 1) + "\" in \""
- + text + '\"');
- }
- value = getProperty(value, segments.get(i));
- }
-
- return coerce(value, type);
- }
-
- };
+ public ValueExpression parse(String expression, Class<?> type) {
+ if (type == JSONArray.class) {
+ // TODO: the coming version of JUEL offers support for custom type converters. Use it!
+ return new CustomCoerce(factory.createValueExpression(parseContext, expression, String.class),
+ type);
+ }
+ return factory.createValueExpression(parseContext, expression, type);
+ }
+
+ private ExpressionFactory newExpressionFactory() {
+ Properties properties = new Properties();
+ // TODO: configure cache size?
+ return new ExpressionFactoryImpl(properties);
}
+
+ /**
+ * @return a default ELResolver with functionality needed by all
+ * expression evaluation.
+ */
+ private ELResolver createDefaultELResolver() {
+ CompositeELResolver resolver = new CompositeELResolver();
+ // Resolvers, in the order they will be most commonly accessed.
+ // Moving JsonELResolver to the end makes JSON property resolution twice
+ // as slow, so this is quite important.
+ resolver.add(new JsonELResolver());
+ resolver.add(new MapELResolver());
+ resolver.add(new ListELResolver());
+ resolver.add(new ArrayELResolver());
+ // TODO: bean el resolver?
+
+ return resolver;
+ }
+
+ /**
+ * Class providing custom type coercion for getValue() where needed.
+ * This will be obsolete with JUEL 2.1.1.
+ */
+ static private class CustomCoerce extends ValueExpression {
- private static List<String> splitSegments(String input) {
- List<String> segments = new ArrayList<String>();
- StringBuilder buf = new StringBuilder(16);
- for (int i = 0, j = input.length(); i < j; ++i) {
- char ch = input.charAt(i);
- if (ch == '\\' && i < j && input.charAt(i + 1) == '.') {
- // Escaped dot.
- buf.append('.');
- ++i;
- } else if (ch == '.') {
- // end of identifier
- segments.add(buf.toString());
- buf.setLength(0);
- } else {
- buf.append(input.charAt(i));
- }
- }
-
- segments.add(buf.toString());
+ private final ValueExpression base;
+ private final Class<?> type;
- return segments;
- }
-
- private static Object getProperty(Object value, String propertyName) throws ElException {
- if (value instanceof Map) {
- Map<?, ?> map = (Map<?, ?>) value;
- return map.get(propertyName);
- } else if (value instanceof JSONObject) {
- return ((JSONObject) value).opt(propertyName);
- } else if (value instanceof ExpressionContext) {
- return ((ExpressionContext) value).getVariable(propertyName);
+ public CustomCoerce(ValueExpression base, Class<?> type) {
+ this.base = base;
+ this.type = type;
}
- throw new ElException("Unsupported property parent type " + value.getClass());
- }
+ @Override
+ public Class<?> getExpectedType() {
+ return type;
+ }
- /**
- * Hardcoded Object-to-Object coercion logic.
- * - Strings are parsed with toString().
- * - Integers are parsed from numbers with intValue(), and with toString() and
- * Integer.parseInt() for all other types.
- * - Booleans are false if they are numeric and equal to 0, if they are
- * Boolean.FALSE, or if they are case-insensitive equal to "false".
- * - Arrays are parsed with comma separators.
- *
- */
- static <T> T coerce(Object value, Class<T> type) throws ElException {
- if (value == null) {
- return null;
+ @Override
+ public Class<?> getType(ELContext context) {
+ return type;
}
- if (type == String.class) {
- @SuppressWarnings("unchecked")
- T string = (T) value.toString();
- return string;
- } else if (type == Integer.class) {
- int intValue;
- if (value instanceof Number) {
- intValue = ((Number) value).intValue();
- } else {
- try {
- intValue = Integer.parseInt(value.toString());
- } catch (NumberFormatException nfe) {
- throw new ElException(nfe);
- }
+ @Override
+ public Object getValue(ELContext context) {
+ Object value = base.getValue(context);
+ if (value == null) {
+ return null;
}
-
- @SuppressWarnings("unchecked")
- T integer = (T) Integer.valueOf(intValue);
- return integer;
- } else if (type == JSONArray.class) {
- JSONArray array;
- if (value instanceof JSONArray) {
- array = (JSONArray) value;
- } else {
- array = new JSONArray();
+
+ if (type == JSONArray.class) {
+ JSONArray array = new JSONArray();
StringTokenizer tokenizer = new StringTokenizer(value.toString(), ",");
while (tokenizer.hasMoreTokens()) {
array.put(tokenizer.nextToken());
}
- }
- @SuppressWarnings("unchecked")
- T t = (T) array;
- return t;
- } else if (type == Boolean.class) {
- boolean boolValue;
- // TODO: spec question, does this coercion make sense?
- if (value instanceof Number) {
- boolValue = ((Number) value).intValue() != 0;
- } else if (value instanceof Boolean) {
- boolValue = Boolean.TRUE.equals(value);
+ return array;
} else {
- // TODO: especially this case...
- boolValue = !"false".equalsIgnoreCase(value.toString());
+ throw new ELException("Can't coerce to type " + type.getName());
}
-
- @SuppressWarnings("unchecked")
- T t = (T) Boolean.valueOf(boolValue);
- return t;
}
- // Fallback, see if the type is already correct
- try {
- return type.cast(value);
- } catch (ClassCastException cce) {
- throw new ElException("Could not cast " + value + " to " + type.getCanonicalName(),
- cce);
+ @Override
+ public boolean isReadOnly(ELContext context) {
+ return true;
}
- }
-
- /** Find the start of the next expression */
- private static int nextExpression(String text, int after) {
- // TODO: JSP EL supports escaping. The Pipelining spec does not.
- // Add detection of "\${" to the spec?
- return text.indexOf(EXPRESSION_START, after);
- }
-
- /** Find the end of the current expression */
- private static int endOfExpression(String text, int after) {
- // TODO: support escaping
- return text.indexOf(EXPRESSION_END, after);
- }
-
- /** Expression class for constant values */
- public static class ConstantExpression<T> implements Expression<T> {
- private final T value;
- public ConstantExpression(T value) {
- this.value = value;
+ @Override
+ public void setValue(ELContext context, Object value) {
+ throw new PropertyNotWritableException();
}
- public T evaluate(ExpressionContext context) {
- return value;
+ @Override
+ public String getExpressionString() {
+ return base.getExpressionString();
}
- }
-
- /** Expression class for string concatenation */
- public static class ConcatExpression<T> implements Expression<T> {
-
- private final List<Expression<String>> expressions;
- private final Class<T> type;
- public ConcatExpression(List<Expression<String>> expressions, Class<T> type) {
- this.expressions = expressions;
- this.type = type;
+ @Override
+ public boolean isLiteralText() {
+ return base.isLiteralText();
}
- public T evaluate(ExpressionContext context) throws ElException {
- StringBuilder builder = new StringBuilder();
- for (Expression<String> expression : expressions) {
- builder.append(expression.evaluate(context));
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+
+ if (!(o instanceof CustomCoerce)) {
+ return false;
}
+
+ CustomCoerce that = (CustomCoerce) o;
+ return that.base.equals(this.base) && that.type.equals(this.type);
+ }
- return coerce(builder.toString(), type);
+ @Override
+ public int hashCode() {
+ return base.hashCode();
}
+
}
}
Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/JsonELResolver.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/JsonELResolver.java?rev=741354&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/JsonELResolver.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/JsonELResolver.java Fri Feb 6 00:31:19 2009
@@ -0,0 +1,131 @@
+/*
+ * 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.shindig.expressions;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+
+/**
+ * ELResolver implementation for JSONArray and JSONObject.
+ */
+class JsonELResolver extends ELResolver {
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ if (base instanceof JSONArray) {
+ return Integer.class;
+ }
+
+ if (base instanceof JSONObject) {
+ return String.class;
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
+ Object base) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if (isJson(base)) {
+ context.setPropertyResolved(true);
+ Object value = getValue(context, base, property);
+ return value == null ? null : value.getClass();
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (base instanceof JSONObject) {
+ context.setPropertyResolved(true);
+ return ((JSONObject) base).opt(String.valueOf(property));
+ }
+
+ if (base instanceof JSONArray) {
+ context.setPropertyResolved(true);
+ int index = toInt(property);
+ return ((JSONArray) base).opt(index);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if (isJson(base)) {
+ context.setPropertyResolved(true);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ if (base instanceof JSONObject) {
+ context.setPropertyResolved(true);
+ try {
+ ((JSONObject) base).put(String.valueOf(property), value);
+ } catch (JSONException e) {
+ throw new ELException(e);
+ }
+ context.setPropertyResolved(true);
+ }
+
+ if (base instanceof JSONArray) {
+ context.setPropertyResolved(true);
+ int index = toInt(property);
+ try {
+ ((JSONArray) base).put(index, value);
+ } catch (JSONException e) {
+ throw new ELException(e);
+ }
+ context.setPropertyResolved(true);
+ }
+ }
+
+ private int toInt(Object property) {
+ if (property instanceof Number) {
+ return ((Number) property).intValue();
+ }
+
+ try {
+ return Integer.parseInt(String.valueOf(property));
+ } catch (NumberFormatException nfe) {
+ throw new ELException(nfe);
+ }
+ }
+
+ private boolean isJson(Object base) {
+ return (base instanceof JSONObject || base instanceof JSONArray);
+ }
+}
Added: incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/RootELResolver.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/RootELResolver.java?rev=741354&view=auto
==============================================================================
--- incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/RootELResolver.java (added)
+++ incubator/shindig/trunk/java/common/src/main/java/org/apache/shindig/expressions/RootELResolver.java Fri Feb 6 00:31:19 2009
@@ -0,0 +1,104 @@
+/*
+ * 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.shindig.expressions;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+/**
+ * ELResolver implementation that adds a map of top-level variables.
+ * New variables can be inserted after creation with:
+ * {@code context.getELResolver().setValue(context, null, name, value);}
+ *
+ * TODO: should this be read-only?
+ *
+ * @see Expressions#newELContext(ELResolver...)
+ */
+public class RootELResolver extends ELResolver {
+ private final Map<String, Object> map;
+
+ public RootELResolver() {
+ this(ImmutableMap.<String, Object>of());
+ }
+
+ public RootELResolver(Map<String, ? extends Object> base) {
+ // TODO: if read-only is OK, then a copy is unnecessary
+ map = Maps.newHashMap(base);
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ if (base == null) {
+ return String.class;
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
+ Object base) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if (base == null) {
+ context.setPropertyResolved(true);
+ Object value = map.get(property);
+ return value == null ? null : value.getClass();
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (base == null) {
+ context.setPropertyResolved(true);
+ return map.get(property);
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if (base == null) {
+ context.setPropertyResolved(true);
+ }
+
+ return false;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ if (base == null) {
+ context.setPropertyResolved(true);
+ map.put(property.toString(), value);
+ }
+ }
+}
Modified: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java (original)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/config/JsonContainerConfigTest.java Fri Feb 6 00:31:19 2009
@@ -25,6 +25,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import org.apache.shindig.expressions.Expressions;
import org.json.JSONObject;
import org.junit.Test;
@@ -53,7 +54,7 @@
private static final String ARRAY_NAME = "array value";
private static final String[] ARRAY_VALUE = new String[]{"Hello", "World"};
private static final String ARRAY_ALT_VALUE = "Not an array";
-
+
private File createContainer(JSONObject json) throws Exception {
File file = File.createTempFile(getClass().getName(), ".json");
file.deleteOnExit();
@@ -81,7 +82,8 @@
@Test
public void parseBasicConfig() throws Exception {
- ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath());
+ ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath(),
+ new Expressions());
assertEquals(1, config.getContainers().size());
for (String container : config.getContainers()) {
@@ -106,7 +108,7 @@
File childFile = createContainer(json);
ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
- JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath());
+ JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), new Expressions());
assertEquals(NESTED_VALUE, config.getString(CONTAINER_A, NESTED_KEY));
assertEquals(NESTED_VALUE, config.getString(CONTAINER_B, NESTED_KEY));
@@ -128,7 +130,7 @@
File childFile = createContainer(json);
File parentFile = createDefaultContainer();
ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
- JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath());
+ JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), new Expressions());
String value = config.getString(CHILD_CONTAINER, TOP_LEVEL_NAME);
assertEquals(TOP_LEVEL_VALUE, value);
@@ -154,7 +156,8 @@
@Test
public void invalidContainerReturnsNull() throws Exception {
- ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath());
+ ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath(),
+ new Expressions());
assertNull("Did not return null for invalid container.", config.getString("fake", PARENT_KEY));
}
@@ -165,13 +168,13 @@
json.put(PARENT_KEY, "bad bad bad parent!");
json.put(ARRAY_NAME, ARRAY_ALT_VALUE);
- new JsonContainerConfig(createContainer(json).getAbsolutePath());
+ new JsonContainerConfig(createContainer(json).getAbsolutePath(), new Expressions());
}
@Test
public void pathQuery() throws Exception {
- ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath());
- String path = "${" + NESTED_KEY + '.' + NESTED_NAME + '}';
+ ContainerConfig config = new JsonContainerConfig(createDefaultContainer().getAbsolutePath(), new Expressions());
+ String path = "${" + NESTED_KEY + "['" + NESTED_NAME + "']}";
String data = config.getString(DEFAULT_CONTAINER, path);
assertEquals(NESTED_VALUE, data);
}
@@ -184,8 +187,24 @@
json.put("expression", "Hello, ${world}!");
json.put("world", "Earth");
- ContainerConfig config = new JsonContainerConfig(createContainer(json).getAbsolutePath());
+ ContainerConfig config = new JsonContainerConfig(createContainer(json).getAbsolutePath(), new Expressions());
assertEquals("Hello, Earth!", config.getString(DEFAULT_CONTAINER, "expression"));
}
+
+ @Test
+ public void expressionEvaluationUsingParent() throws Exception {
+ // We use a JSON Object here to guarantee that we're well formed up front.
+ JSONObject json = new JSONObject();
+ json.put(CONTAINER_KEY, new String[]{CHILD_CONTAINER});
+ json.put(PARENT_KEY, DEFAULT_CONTAINER);
+ json.put("parentExpression", "${parent['" + TOP_LEVEL_NAME + "']}");
+
+ File childFile = createContainer(json);
+ File parentFile = createDefaultContainer();
+ ContainerConfig config = new JsonContainerConfig(childFile.getAbsolutePath() +
+ JsonContainerConfig.FILE_SEPARATOR + parentFile.getAbsolutePath(), new Expressions());
+
+ assertEquals(TOP_LEVEL_VALUE, config.getString(CHILD_CONTAINER, "parentExpression"));
+ }
}
Modified: incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/expressions/ExpressionsTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/expressions/ExpressionsTest.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/expressions/ExpressionsTest.java (original)
+++ incubator/shindig/trunk/java/common/src/test/java/org/apache/shindig/expressions/ExpressionsTest.java Fri Feb 6 00:31:19 2009
@@ -19,136 +19,84 @@
package org.apache.shindig.expressions;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
+import org.json.JSONArray;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
-import java.util.Map;
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
-/**
- * Tests of the Expressions class.
- */
public class ExpressionsTest {
- private FakeContext context;
-
+ private Expressions expressions;
+ private ELContext context;
+
@Before
public void setUp() {
- context = new FakeContext();
+ expressions = new Expressions();
+ context = expressions.newELContext(new RootELResolver());
}
-
+
@Test
- public void constantExpressions() throws Exception {
- assertEquals("foo", Expressions.parse("foo", String.class).evaluate(context));
- assertEquals(1, Expressions.parse("1", Integer.class).evaluate(context).intValue());
+ public void arraySupport() {
+ addVariable("array", new String[]{"foo", "bar"});
+ String result = evaluate("${array[0]}${array[1]}", String.class);
+ assertEquals("foobar", result);
}
-
+
@Test
- public void simpleExpressions() throws Exception {
- context.variables.put("var", "value");
- context.variables.put("int", 1);
-
- Expression<String> var = Expressions.parse("${var}", String.class);
- assertEquals("value", var.evaluate(context));
-
- Expression<Integer> intExpression = Expressions.parse("${int}", Integer.class);
- assertEquals(1, intExpression.evaluate(context).intValue());
+ public void listSupport() {
+ addVariable("list", ImmutableList.of("foo", "bar"));
+ String result = evaluate("${list[0]}${list[1]}", String.class);
+ assertEquals("foobar", result);
}
-
+
@Test
- public void variableNotFoundIsNull() throws Exception {
- Expression<String> var = Expressions.parse("${var}", String.class);
- assertNull(var.evaluate(context));
+ public void mapSupport() {
+ addVariable("map", ImmutableMap.of("foo", "bar"));
+ String result = evaluate("${map.foo}${map['foo']}", String.class);
+ assertEquals("barbar", result);
}
@Test
- public void propertyEvaluationForMaps() throws Exception {
- context.variables.put("var", ImmutableMap.of("one", 1, "two", 2));
-
- Expression<Integer> var = Expressions.parse("${var.one}${var.two}", Integer.class);
- // 1 and 2 concatenated make 12, not 3
- assertEquals(12, var.evaluate(context).intValue());
+ public void jsonObjectSupport() throws Exception {
+ addVariable("object", new JSONObject("{foo: 125}"));
+ int result = evaluate("${object.foo}", Integer.class);
+ assertEquals(125, result);
}
@Test
- public void propertyEvaluationForJson() throws Exception {
- context.variables.put("var", new JSONObject("{top: {middle: {inner: 'value'}}}"));
-
- Expression<String> var = Expressions.parse("${var.top.middle.inner}", String.class);
- assertEquals("value", var.evaluate(context));
+ public void jsonArraySupport() throws Exception {
+ addVariable("array", new JSONArray("[1, 2]"));
+ int result = evaluate("${array[0] + array[1]}", Integer.class);
+ assertEquals(3, result);
}
@Test
- public void propertyEvaluationForExpressionContext() throws Exception {
- context.variables.put("var", new ExpressionContext() {
- public Object getVariable(String name) {
- return name.equals("top") ? "value" : null;
- }
- });
-
- Expression<String> var = Expressions.parse("${var.top}", String.class);
- assertEquals("value", var.evaluate(context));
+ public void jsonArrayCoercionOfStatic() throws Exception {
+ JSONArray result = evaluate("first,second", JSONArray.class);
+ JSONArray expected = new JSONArray("['first', 'second']");
+ assertEquals(expected.toString(), result.toString());
}
-
+
@Test
- public void propertyEvaluationWithEscapedDot() throws Exception {
- context.variables.put("foo.bar", "baz");
- Expression<String> var = Expressions.parse("${foo\\.bar}", String.class);
- assertEquals("baz", var.evaluate(context));
+ public void jsonArrayCoercion() throws Exception {
+ addVariable("foo", "first,second");
+ JSONArray result = evaluate("${foo}", JSONArray.class);
+ JSONArray expected = new JSONArray("['first', 'second']");
+ assertEquals(expected.toString(), result.toString());
}
-
- @Test(expected = ElException.class)
- public void propertyEvaluationWithLeadingDot() throws Exception {
- context.variables.put("foo", "bar");
- Expression<String> var = Expressions.parse("${.foo}", String.class);
- var.evaluate(context);
+
+ private <T> T evaluate(String expression, Class<T> type) {
+ ValueExpression expr = expressions.parse(expression, type);
+ return type.cast(expr.getValue(context));
}
- @Test(expected = ElException.class)
- public void propertyEvaluationWithTrailingDot() throws Exception {
- context.variables.put("foo", "bar");
- Expression<String> var = Expressions.parse("${foo.}", String.class);
- var.evaluate(context);
+ private void addVariable(String key, Object value) {
+ context.getELResolver().setValue(context, null, key, value);
}
-
- @Test(expected = ElException.class)
- public void exceptionWhenCoercionFails() throws Exception {
- context.variables.put("var", "value");
-
- Expression<Integer> var = Expressions.parse("${var}", Integer.class);
- var.evaluate(context);
- }
-
- @Test(expected = ElException.class)
- public void exceptionIfExpressionNotClosed() throws Exception {
- Expressions.parse("${var${foo", Integer.class);
- }
-
- @Test(expected = ElException.class)
- public void exceptionWhenPropertyEvaluationFails() throws Exception {
- Expression<String> var = Expressions.parse("${var.property}", String.class);
- var.evaluate(context);
- }
-
- @Test
- public void concatExpressions() throws Exception {
- context.variables.put("var", "value");
-
- Expression<String> concat = Expressions.parse("foo${var}bar", String.class);
- assertEquals("foovaluebar", concat.evaluate(context));
- }
-
- static public class FakeContext implements ExpressionContext {
- public final Map<String, Object> variables = Maps.newHashMap();
-
- public Object getVariable(String name) {
- return variables.get(name);
- }
- }
-
}
Added: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetELResolver.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetELResolver.java?rev=741354&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetELResolver.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/GadgetELResolver.java Fri Feb 6 00:31:19 2009
@@ -0,0 +1,127 @@
+/*
+ * 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.shindig.gadgets;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.PropertyNotWritableException;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * ELResolver for the built-in gadget properties:
+ * - UserPrefs: the user preferences
+ * - ViewParams: view params (as a JSON object)
+ */
+public class GadgetELResolver extends ELResolver {
+ public static final String USER_PREFS_PROPERTY = "UserPrefs";
+ public static final String VIEW_PARAMS_PROPERTY = "ViewParams";
+
+ private final GadgetContext gadgetContext;
+
+ public GadgetELResolver(GadgetContext context) {
+ this.gadgetContext = context;
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ if (base == null) {
+ return String.class;
+ }
+
+ return null;
+ }
+
+ @Override
+ public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
+ Object base) {
+ return null;
+ }
+
+ @Override
+ public Class<?> getType(ELContext context, Object base, Object property) {
+ if (base == null) {
+ if (VIEW_PARAMS_PROPERTY.equals(property)) {
+ context.setPropertyResolved(true);
+ return Object.class;
+ } else if (USER_PREFS_PROPERTY.equals(property)) {
+ context.setPropertyResolved(true);
+ return Map.class;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (base == null) {
+ if (VIEW_PARAMS_PROPERTY.equals(property)) {
+ context.setPropertyResolved(true);
+ String params = gadgetContext.getParameter("view-params");
+ if (params != null && !"".equals(params)) {
+ try {
+ // TODO: immutable?
+ return new JSONObject(params);
+ } catch (JSONException e) {
+ throw new ELException(e);
+ }
+ }
+
+ // Return an empty map - this doesn't allocate anything, whereas an
+ // empty JSONObject would
+ return ImmutableMap.of();
+ } else if (USER_PREFS_PROPERTY.equals(property)) {
+ context.setPropertyResolved(true);
+ // TODO: immutable?
+ return gadgetContext.getUserPrefs().getPrefs();
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if ((base == null) &&
+ (VIEW_PARAMS_PROPERTY.equals(property)
+ || USER_PREFS_PROPERTY.equals(property))) {
+ context.setPropertyResolved(true);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property, Object value) {
+ if ((base == null) &&
+ (VIEW_PARAMS_PROPERTY.equals(property)
+ || USER_PREFS_PROPERTY.equals(property))) {
+ throw new PropertyNotWritableException("Cannot override " + property);
+ }
+ }
+}
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/process/Processor.java Fri Feb 6 00:31:19 2009
@@ -98,7 +98,7 @@
View view = spec.getView(viewName);
if (view == null) {
String container = context.getContainer();
- String property = "${gadgets\\.features.views." + viewName + ".aliases}";
+ String property = "${Cur['gadgets.features'].views['" + viewName + "'].aliases}";
for (Object alias : containerConfig.getList(container, property)) {
viewName = alias.toString();
view = spec.getView(viewName);
Modified: incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/spec/PipelinedData.java Fri Feb 6 00:31:19 2009
@@ -17,23 +17,23 @@
*/
package org.apache.shindig.gadgets.spec;
-import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.common.xml.XmlException;
-import org.apache.shindig.expressions.ElException;
-import org.apache.shindig.expressions.Expression;
-import org.apache.shindig.expressions.ExpressionContext;
-import org.apache.shindig.expressions.Expressions;
-import org.apache.shindig.gadgets.AuthType;
-import org.apache.shindig.gadgets.GadgetContext;
-import org.apache.shindig.gadgets.GadgetExpressionContext;
-import org.apache.shindig.gadgets.variables.Substitutions;
-
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ValueExpression;
+
+import org.apache.shindig.common.uri.Uri;
+import org.apache.shindig.common.xml.XmlException;
+import org.apache.shindig.expressions.Expressions;
+import org.apache.shindig.gadgets.AuthType;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetELResolver;
+import org.apache.shindig.gadgets.variables.Substitutions;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -99,7 +99,7 @@
// notImplemented
throw new SpecParserException("Unknown element <os:" + elementName + '>');
}
- } catch (ElException ele) {
+ } catch (ELException ele) {
throw new SpecParserException(new XmlException(ele));
}
}
@@ -135,11 +135,12 @@
public Map<String, Object> getSocialPreloads(GadgetContext context) {
Map<String, Object> evaluatedPreloads = Maps.newHashMapWithExpectedSize(
socialPreloads.size());
- ExpressionContext expressionContext = new GadgetExpressionContext(context);
+ Expressions expressions = Expressions.sharedInstance();
+ ELContext elContext = expressions.newELContext(new GadgetELResolver(context));
for (Map.Entry<String, SocialData> preload : socialPreloads.entrySet()) {
try {
- evaluatedPreloads.put(preload.getKey(), preload.getValue().toJson(expressionContext));
- } catch (ElException e) {
+ evaluatedPreloads.put(preload.getKey(), preload.getValue().toJson(elContext));
+ } catch (ELException e) {
// TODO: Handle!?!
throw new RuntimeException(e);
}
@@ -151,11 +152,12 @@
public Map<String, RequestAuthenticationInfo> getHttpPreloads(GadgetContext context) {
Map<String, RequestAuthenticationInfo> evaluatedPreloads = Maps.newHashMapWithExpectedSize(
httpPreloads.size());
- ExpressionContext expressionContext = new GadgetExpressionContext(context);
+ Expressions expressions = Expressions.sharedInstance();
+ ELContext elContext = expressions.newELContext(new GadgetELResolver(context));
for (Map.Entry<String, HttpData> preload : httpPreloads.entrySet()) {
try {
- evaluatedPreloads.put(preload.getKey(), preload.getValue().evaluate(expressionContext));
- } catch (ElException e) {
+ evaluatedPreloads.put(preload.getKey(), preload.getValue().evaluate(elContext));
+ } catch (ELException e) {
// TODO: Handle!?!
throw new RuntimeException(e);
}
@@ -173,7 +175,7 @@
}
/** Handle the os:PeopleRequest element */
- private SocialData createPeopleRequest(Element child) throws ElException {
+ private SocialData createPeopleRequest(Element child) throws ELException {
SocialData expression = new SocialData(child.getAttribute("key"), "people.get");
copyAttribute("groupId", child, expression, String.class);
@@ -195,16 +197,16 @@
}
/** Handle the os:ViewerRequest element */
- private SocialData createViewerRequest(Element child) throws ElException {
+ private SocialData createViewerRequest(Element child) throws ELException {
return createPersonRequest(child, "@viewer");
}
/** Handle the os:OwnerRequest element */
- private SocialData createOwnerRequest(Element child) throws ElException {
+ private SocialData createOwnerRequest(Element child) throws ELException {
return createPersonRequest(child, "@owner");
}
- private SocialData createPersonRequest(Element child, String userId) throws ElException {
+ private SocialData createPersonRequest(Element child, String userId) throws ELException {
SocialData expression = new SocialData(child.getAttribute("key"), "people.get");
expression.addProperty("userId", userId, JSONArray.class);
@@ -215,7 +217,7 @@
}
/** Handle the os:PersonAppDataRequest element */
- private SocialData createPersonAppDataRequest(Element child) throws ElException {
+ private SocialData createPersonAppDataRequest(Element child) throws ELException {
SocialData expression = new SocialData(child.getAttribute("key"), "appdata.get");
copyAttribute("groupId", child, expression, String.class);
@@ -228,7 +230,7 @@
}
/** Handle the os:ActivitiesRequest element */
- private SocialData createActivityRequest(Element child) throws ElException {
+ private SocialData createActivityRequest(Element child) throws ELException {
SocialData expression = new SocialData(child.getAttribute("key"), "activities.get");
copyAttribute("groupId", child, expression, String.class);
@@ -243,7 +245,7 @@
}
/** Handle an os:MakeRequest element */
- private HttpData createMakeRequest(Element child, Uri base) throws ElException {
+ private HttpData createMakeRequest(Element child, Uri base) throws ELException {
HttpData data = new HttpData(child, base);
/* TODO: check auth type, and sign-by-owner/viewer, once spec agrees
@@ -262,7 +264,7 @@
}
private void copyAttribute(String name, Element element, SocialData expression, Class<?> type)
- throws ElException {
+ throws ELException {
if (element.hasAttribute(name)) {
expression.addProperty(name, element.getAttribute(name), type);
}
@@ -292,12 +294,12 @@
* A single pipelined HTTP makerequest.
*/
private static class HttpData {
- private final Expression<String> authz;
+ private final String authz;
private final Uri base;
private final String href;
- private final Expression<Boolean> signOwner;
- private final Expression<Boolean> signViewer;
- private final Map<String, Expression<String>> attributes;
+ private final boolean signOwner;
+ private final boolean signViewer;
+ private final Map<String, ValueExpression> attributes;
private static final Set<String> KNOWN_ATTRIBUTES =
ImmutableSet.of("authz", "href", "sign_owner", "sign_viewer");
@@ -305,25 +307,27 @@
/**
* Create an HttpData off an <os:makeRequest> element.
*/
- public HttpData(Element element, Uri base) throws ElException {
+ public HttpData(Element element, Uri base) throws ELException {
this.base = base;
- // TODO: Spec question; should authz be EL enabled?
- String authz = element.hasAttribute("authz") ? element.getAttribute("authz") : "none";
- this.authz = Expressions.parse(authz, String.class);
+ this.authz = element.hasAttribute("authz") ? element.getAttribute("authz") : "none";
// TODO: Spec question; should EL values be URL escaped?
this.href = element.getAttribute("href");
- // TODO: Spec question; should sign_* be EL enabled?
- this.signOwner = booleanExpression(element, "sign_owner");
- this.signViewer = booleanExpression(element, "sign_viewer");
-
- Map<String, Expression<String>> attributes = Maps.newHashMap();
+ // TODO: Spec question; should sign_* default to true?
+ this.signOwner = booleanValue(element, "sign_owner", true);
+ this.signViewer = booleanValue(element, "sign_viewer", true);
+
+ Expressions expressions = Expressions.sharedInstance();
+
+ // TODO: many of these attributes should not be EL enabled
+ Map<String, ValueExpression> attributes = Maps.newHashMap();
for (int i = 0; i < element.getAttributes().getLength(); i++) {
Node attr = element.getAttributes().item(i);
if (!KNOWN_ATTRIBUTES.contains(attr.getNodeName())) {
- attributes.put(attr.getNodeName(), Expressions.parse(attr.getNodeValue(), String.class));
+ attributes.put(attr.getNodeName(),
+ expressions.parse(attr.getNodeValue(), String.class));
}
}
@@ -346,20 +350,19 @@
/**
* Evaluate expressions and return a RequestAuthenticationInfo.
- * @throws ElException if expression evaluation fails.
+ * @throws ELException if expression evaluation fails.
*/
- public RequestAuthenticationInfo evaluate(ExpressionContext context) throws ElException {
- final AuthType authType = AuthType.parse(authz.evaluate(context));
- Expression<String> hrefExpression = Expressions.parse(href, String.class);
- final Uri evaluatedHref = base.resolve(Uri.parse(hrefExpression.evaluate(context)));
-
- final boolean evaluatedSignOwner = evaluateBooleanExpression(context,
- this.signOwner, true);
- final boolean evaluatedSignViewer = evaluateBooleanExpression(context,
- this.signViewer, true);
+ public RequestAuthenticationInfo evaluate(ELContext context) throws ELException {
+ final AuthType authType = AuthType.parse(authz);
+
+ Expressions expressions = Expressions.sharedInstance();
+ String hrefString = String.valueOf(expressions.parse(href, String.class)
+ .getValue(context));
+ final Uri evaluatedHref = base.resolve(Uri.parse(hrefString));
+
final Map<String, String> evaluatedAttributes = Maps.newHashMap();
- for (Map.Entry<String, Expression<String>> attr : attributes.entrySet()) {
- evaluatedAttributes.put(attr.getKey(), attr.getValue().evaluate(context));
+ for (Map.Entry<String, ValueExpression> attr : attributes.entrySet()) {
+ evaluatedAttributes.put(attr.getKey(), (String) attr.getValue().getValue(context));
}
return new RequestAuthenticationInfo() {
@@ -376,38 +379,23 @@
}
public boolean isSignOwner() {
- return evaluatedSignOwner;
+ return signOwner;
}
public boolean isSignViewer() {
- return evaluatedSignViewer;
+ return signViewer;
}
};
}
- /** Evaluate a boolean expression, choosing a default value if needed */
- private boolean evaluateBooleanExpression(ExpressionContext context,
- Expression<Boolean> expression, boolean defaultValue) throws ElException {
- if (expression == null) {
- return defaultValue;
- }
-
- Boolean value = expression.evaluate(context);
- if (value == null) {
- return defaultValue;
- }
-
- return value;
- }
-
/** Parse a boolean expression off an XML attribute. */
- private Expression<Boolean> booleanExpression(Element element, String attrName)
- throws ElException {
+ private boolean booleanValue(Element element, String attrName,
+ boolean defaultValue) {
if (!element.hasAttribute(attrName)) {
- return null;
+ return defaultValue;
}
- return Expressions.parse(element.getAttribute(attrName), Boolean.class);
+ return "true".equalsIgnoreCase(element.getAttribute(attrName));
}
}
@@ -424,13 +412,13 @@
this.method = method;
}
- public void addProperty(String name, String value, Class<?> type) throws ElException {
- Expression<?> expression = Expressions.parse(value, type);
+ public void addProperty(String name, String value, Class<?> type) throws ELException {
+ ValueExpression expression = Expressions.sharedInstance().parse(value, type);
properties.add(new Property(name, expression));
}
/** Create the JSON request form for the social data */
- public JSONObject toJson(ExpressionContext context) throws ElException {
+ public JSONObject toJson(ELContext elContext) throws ELException {
JSONObject object = new JSONObject();
try {
object.put("method", method);
@@ -438,11 +426,11 @@
JSONObject params = new JSONObject();
for (Property property : properties) {
- property.set(context, params);
+ property.set(elContext, params);
}
object.put("params", params);
} catch (JSONException je) {
- throw new ElException(je);
+ throw new ELException(je);
}
return object;
@@ -450,22 +438,22 @@
/** Single property for an expression */
private static class Property {
- private final Expression<?> expression;
+ private final ValueExpression expression;
private final String name;
- public Property(String name, Expression<?> expression) {
+ public Property(String name, ValueExpression expression) {
this.name = name;
this.expression = expression;
}
- public void set(ExpressionContext context, JSONObject object) throws ElException {
- Object value = expression.evaluate(context);
+ public void set(ELContext elContext, JSONObject object) throws ELException {
+ Object value = expression.getValue(elContext);
try {
if (value != null) {
object.put(name, value);
}
} catch (JSONException e) {
- throw new ElException("Error parsing property \"" + name + '\"', e);
+ throw new ELException("Error parsing property \"" + name + '\"', e);
}
}
}
Added: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetELResolverTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetELResolverTest.java?rev=741354&view=auto
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetELResolverTest.java (added)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/GadgetELResolverTest.java Fri Feb 6 00:31:19 2009
@@ -0,0 +1,100 @@
+/*
+ * 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.shindig.gadgets;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.shindig.expressions.Expressions;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.ValueExpression;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * Test of GadgetELResolver.
+ */
+public class GadgetELResolverTest {
+ private UserPrefs userPrefs;
+ private String viewParams;
+ private ELResolver resolver;
+ private Expressions expressions;
+ private ELContext context;
+
+ @Before
+ public void setUp() throws Exception {
+ GadgetContext gadgetContext = new GadgetContext() {
+ @Override
+ public String getParameter(String name) {
+ if ("view-params".equals(name)) {
+ return viewParams;
+ }
+
+ return null;
+ }
+
+ @Override
+ public UserPrefs getUserPrefs() {
+ return userPrefs;
+ }
+ };
+
+ resolver = new GadgetELResolver(gadgetContext);
+ expressions = new Expressions();
+
+ context = expressions.newELContext(resolver);
+ }
+
+ @Test
+ public void getPrefs() {
+ userPrefs = new UserPrefs(ImmutableMap.of("foo", "bar"));
+ ValueExpression expression = expressions.parse("${UserPrefs.foo}", String.class);
+
+ assertEquals("bar", expression.getValue(context));
+
+ expression = expressions.parse("${UserPrefs.wrongKey}", String.class);
+ assertEquals("", expression.getValue(context));
+ }
+
+ @Test
+ public void getPrefsEmpty() {
+ userPrefs = UserPrefs.EMPTY;
+ ValueExpression expression = expressions.parse("${UserPrefs.foo}", String.class);
+ assertEquals("", expression.getValue(context));
+ }
+
+ @Test
+ public void testViewParams() {
+ viewParams = "{foo: 'bar'}";
+
+ ValueExpression expression = expressions.parse("${ViewParams.foo}", String.class);
+ assertEquals("bar", expression.getValue(context));
+
+ expression = expressions.parse("${ViewParams.wrongKey}", String.class);
+ assertEquals("", expression.getValue(context));
+ }
+
+ @Test
+ public void testViewParamsEmpty() {
+ ValueExpression expression = expressions.parse("${ViewParams.foo}", String.class);
+ assertEquals("", expression.getValue(context));
+ }
+}
Modified: incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java (original)
+++ incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/process/ProcessorTest.java Fri Feb 6 00:31:19 2009
@@ -22,7 +22,9 @@
import static org.junit.Assert.assertTrue;
import org.apache.shindig.common.uri.Uri;
-import org.apache.shindig.config.AbstractContainerConfig;
+import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.config.JsonContainerConfig;
+import org.apache.shindig.expressions.Expressions;
import org.apache.shindig.gadgets.Gadget;
import org.apache.shindig.gadgets.GadgetBlacklist;
import org.apache.shindig.gadgets.GadgetContext;
@@ -30,17 +32,11 @@
import org.apache.shindig.gadgets.GadgetSpecFactory;
import org.apache.shindig.gadgets.spec.GadgetSpec;
import org.apache.shindig.gadgets.variables.VariableSubstituter;
-
-import com.google.common.collect.Maps;
-
+import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import java.net.URI;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
public class ProcessorTest {
private static final Uri SPEC_URL = Uri.parse("http://example.org/gadget.xml");
@@ -58,12 +54,17 @@
private final FakeVariableSubstituter substituter = new FakeVariableSubstituter();
private final FakeBlacklist blacklist = new FakeBlacklist();
- private FakeContainerConfig containerConfig;
+ private ContainerConfig containerConfig;
private Processor processor;
@Before
public void setUp() throws Exception {
- containerConfig = new FakeContainerConfig();
+ JSONObject config = new JSONObject("{" + ContainerConfig.DEFAULT_CONTAINER + ":" +
+ "{'gadgets.features':{views:" +
+ "{aliased: {aliases: ['some-alias', 'alias']}}" +
+ "}}}");
+
+ containerConfig = new JsonContainerConfig(config, new Expressions());
processor = new Processor(gadgetSpecFactory, substituter, containerConfig, blacklist);
}
@@ -102,8 +103,6 @@
@Test
public void doViewAliasing() throws Exception {
- List<Object> aliases = Arrays.<Object>asList("some-alias", "alias");
- containerConfig.data.put("${gadgets\\.features.views.aliased.aliases}", aliases);
Gadget gadget = processor.process(makeContext("aliased"));
assertEquals(BASIC_HTML_CONTENT, gadget.getCurrentView().getContent());
}
@@ -161,25 +160,6 @@
}
}
- private static class FakeContainerConfig extends AbstractContainerConfig {
- protected final Map<String, Object> data = Maps.newHashMap();
-
- @Override
- public Object getProperty(String container, String parameter) {
- return data.get(parameter);
- }
-
- @Override
- public Collection<String> getContainers() {
- return null;
- }
-
- @Override
- public Map<String, Object> getProperties(String container) {
- return null;
- }
- }
-
private static class FakeGadgetSpecFactory implements GadgetSpecFactory {
protected GadgetException exception;
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/ActivityHandler.java Fri Feb 6 00:31:19 2009
@@ -24,21 +24,18 @@
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
@Service(name = "activities", path="/{userId}+/{groupId}/{appId}/{activityId}+")
public class ActivityHandler {
- private static final Logger logger = Logger.getLogger(ActivityHandler.class.getName());
-
private final ActivityService service;
private final ContainerConfig config;
@@ -139,11 +136,11 @@
}
@Operation(httpMethods = "GET", path="/@supportedFields")
- public List supportedFields(RequestItem request) {
+ public List<Object> supportedFields(RequestItem request) {
// TODO: Would be nice if name in config matched name of service.
String container = Objects.firstNonNull(request.getToken().getContainer(),
ContainerConfig.DEFAULT_CONTAINER);
return config.getList(container,
- "${gadgets\\.features.opensocial-0\\.8.supportedFields.activity}");
+ "${Cur['gadgets.features']['opensocial-0.8'].supportedFields.activity}");
}
}
Modified: incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java (original)
+++ incubator/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/opensocial/service/PersonHandler.java Fri Feb 6 00:31:19 2009
@@ -25,19 +25,17 @@
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Future;
+
import com.google.common.base.Objects;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Future;
-import java.util.logging.Logger;
-
@Service(name = "people", path = "/{userId}+/{groupId}/{personId}+")
public class PersonHandler {
- private final static Logger logger = Logger.getLogger(PersonHandler.class.getName());
private final PersonService personService;
private final ContainerConfig config;
@@ -95,10 +93,10 @@
}
@Operation(httpMethods = "GET", path="/@supportedFields")
- public List supportedFields(RequestItem request) {
+ public List<Object> supportedFields(RequestItem request) {
// TODO: Would be nice if name in config matched name of service.
String container = Objects.firstNonNull(request.getToken().getContainer(), "default");
return config.getList(container,
- "${gadgets\\.features.opensocial-0\\.8.supportedFields.person}");
+ "${Cur['gadgets.features']['opensocial-0.8'].supportedFields.person}");
}
}
Modified: incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java?rev=741354&r1=741353&r2=741354&view=diff
==============================================================================
--- incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java (original)
+++ incubator/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/opensocial/service/ActivityHandlerTest.java Fri Feb 6 00:31:19 2009
@@ -17,10 +17,15 @@
*/
package org.apache.shindig.social.opensocial.service;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.isNull;
+
import org.apache.shindig.common.EasyMockTestCase;
import org.apache.shindig.common.testing.FakeGadgetToken;
import org.apache.shindig.common.util.ImmediateFuture;
import org.apache.shindig.config.ContainerConfig;
+import org.apache.shindig.config.JsonContainerConfig;
+import org.apache.shindig.expressions.Expressions;
import org.apache.shindig.social.core.model.ActivityImpl;
import org.apache.shindig.social.core.util.BeanJsonConverter;
import org.apache.shindig.social.opensocial.model.Activity;
@@ -30,20 +35,18 @@
import org.apache.shindig.social.opensocial.spi.RestfulCollection;
import org.apache.shindig.social.opensocial.spi.SocialSpiException;
import org.apache.shindig.social.opensocial.spi.UserId;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.classextension.EasyMock.eq;
-import static org.easymock.classextension.EasyMock.isNull;
+import org.json.JSONObject;
import java.io.StringReader;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Future;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+
public class ActivityHandlerTest extends EasyMockTestCase {
private BeanJsonConverter converter;
@@ -68,7 +71,13 @@
converter = mock(BeanJsonConverter.class);
activityService = mock(ActivityService.class);
- containerConfig = mock(ContainerConfig.class);
+
+ JSONObject config = new JSONObject("{" + ContainerConfig.DEFAULT_CONTAINER + ":" +
+ "{'gadgets.features':{'opensocial-0.8':" +
+ "{supportedFields: {activity: ['id', 'title']}}" +
+ "}}}");
+
+ containerConfig = new JsonContainerConfig(config, new Expressions());
handler = new ActivityHandler(activityService, containerConfig);
registry = new DefaultHandlerRegistry(null, Lists.newArrayList(handler));
}
@@ -140,7 +149,7 @@
reset();
}
- private Future setupBodyRequest(String method) throws SocialSpiException {
+ private Future<?> setupBodyRequest(String method) throws SocialSpiException {
String jsonActivity = "{title: hi mom!, etc etc}";
String path = "/activities/john.doe/@self/@app";
@@ -160,14 +169,14 @@
}
public void testHandlePost() throws Exception {
- Future future = setupBodyRequest("POST");
+ Future<?> future = setupBodyRequest("POST");
assertNull(future.get());
verify();
reset();
}
public void testHandlePut() throws Exception {
- Future future = setupBodyRequest("PUT");
+ Future<?> future = setupBodyRequest("PUT");
assertNull(future.get());
verify();
reset();
@@ -193,13 +202,14 @@
String path = "/activities/@supportedFields";
RestHandler operation = registry.getRestHandler(path, "GET");
- List<Object> list = ImmutableList.<Object>of("id", "title");
- expect(containerConfig.getList(eq("default"),
- eq("${gadgets\\.features.opensocial-0\\.8.supportedFields.activity}"))).andReturn(list);
-
replay();
- assertEquals(list, operation.execute(path, Maps.<String, String[]>newHashMap(), null,
- token, converter).get());
+ @SuppressWarnings("unchecked")
+ List<Object> received = (List<Object>) operation.execute(path, Maps.<String, String[]>newHashMap(), null,
+ token, converter).get();
+ assertEquals(2, received.size());
+ assertEquals("id", received.get(0).toString());
+ assertEquals("title", received.get(1).toString());
+
verify();
}
}