Author: awiner
Date: Wed Mar 18 23:05:16 2009
New Revision: 755772
URL: http://svn.apache.org/viewvc?rev=755772&view=rev
Log:
SHINDIG-748: OpenSocial templates
Patch from Lev Epshteyn, and cleanup and further work from me, implementing:
- @tag attribute on script blocks
- os:Render element
- ${My} support
- Fix for stack overflow exception in ELResolvers if ${Cur} is null
- @repeat or os:Repeat over a scalary type now iterates once
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java
(with props)
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java
(with props)
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java
(with props)
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java
(with props)
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java
(with props)
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/AbstractTagHandler.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessor.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TagRegistry.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateContext.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateELResolver.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateModule.java
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateProcessor.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/FakeTemplateProcessor.java
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/rewrite/TemplateRewriter.java
Wed Mar 18 23:05:16 2009
@@ -27,6 +27,9 @@
import org.apache.shindig.gadgets.spec.Feature;
import org.apache.shindig.gadgets.spec.MessageBundle;
import org.apache.shindig.gadgets.templates.MessageELResolver;
+import org.apache.shindig.gadgets.templates.TagHandler;
+import org.apache.shindig.gadgets.templates.TagRegistry;
+import org.apache.shindig.gadgets.templates.TemplateBasedTagHandler;
import org.apache.shindig.gadgets.templates.TemplateContext;
import org.apache.shindig.gadgets.templates.TemplateProcessor;
import org.json.JSONObject;
@@ -66,13 +69,16 @@
private final Provider<TemplateProcessor> processor;
private final MessageBundleFactory messageBundleFactory;
private final Expressions expressions;
+ private final TagRegistry baseTagRegistry;
@Inject
public TemplateRewriter(Provider<TemplateProcessor> processor,
- MessageBundleFactory messageBundleFactory, Expressions expressions) {
+ MessageBundleFactory messageBundleFactory, Expressions expressions,
+ TagRegistry baseTagRegistry) {
this.processor = processor;
this.messageBundleFactory = messageBundleFactory;
this.expressions = expressions;
+ this.baseTagRegistry = baseTagRegistry;
}
public RewriterResults rewrite(HttpRequest request, HttpResponse original,
@@ -107,22 +113,65 @@
private RewriterResults rewriteImpl(Gadget gadget, MutableContent content)
throws GadgetException {
- List<Element> tagList =
- DomUtil.getElementsByTagNameCaseInsensitive(content.getDocument(), TAGS);
+ List<Element> templates = ImmutableList.copyOf(
+ Iterables.filter(
+ DomUtil.getElementsByTagNameCaseInsensitive(content.getDocument(),
TAGS),
+ new Predicate<Element>() {
+ public boolean apply(Element element) {
+ return "text/os-template".equals(element.getAttribute("type"));
+ }
+ }));
+
+ TagRegistry registry = registerCustomTags(templates);
+
+ return processInlineTemplates(gadget, content, templates, registry);
+ }
+
+ /**
+ * Register templates with a "tag" attribute.
+ */
+ private TagRegistry registerCustomTags(List<Element> allTemplates) {
+ ImmutableSet.Builder<TagHandler> handlers = ImmutableSet.builder();
+ for (Element template : allTemplates) {
+ // Only process templates with a tag attribute
+ if (template.getAttribute("tag").length() == 0) {
+ continue;
+ }
+
+ // TODO: split() is a regex compile, and should be avoided
+ String [] nameParts = template.getAttribute("tag").split(":");
+ // At this time, we only support
+ if (nameParts.length != 2) {
+ continue;
+ }
+ String namespaceUri = template.lookupPrefix(nameParts[0]);
+ if (namespaceUri == null) {
+ // If the namespace prefix is defined on the template tag, get it from
+ // the attribute.
+ namespaceUri = template.getAttribute("xmlns:" + nameParts[0]);
+ }
+ if (namespaceUri.length() > 0) {
+ handlers.add(new TemplateBasedTagHandler(template, namespaceUri,
nameParts[1]));
+ }
+ }
+
+ return baseTagRegistry.addHandlers(handlers.build());
+ }
+
+ private RewriterResults processInlineTemplates(Gadget gadget, MutableContent
content,
+ List<Element> allTemplates, TagRegistry registry) throws GadgetException
{
final Map<String, JSONObject> pipelinedData = content.getPipelinedData();
List<Element> templates = ImmutableList.copyOf(
- Iterables.filter(tagList, new Predicate<Element>() {
+ Iterables.filter(allTemplates, new Predicate<Element>() {
public boolean apply(Element element) {
- String type = element.getAttribute("type");
String name = element.getAttribute("name");
String tag = element.getAttribute("tag");
String require = element.getAttribute("require");
// Templates with "tag" or "name" can't be processed; templates
// that require data that isn't available on the server can't
// be processed either
- return "text/os-template".equals(type)
- && "".equals(name)
+ return "".equals(name)
&& "".equals(tag)
&& checkRequiredData(require, pipelinedData.keySet());
}
@@ -141,7 +190,7 @@
for (Element template : templates) {
DocumentFragment result = processor.get().processTemplate(
- template, templateContext, messageELResolver);
+ template, templateContext, messageELResolver, registry);
// Note: replaceNode errors when replacing Element with DocumentFragment
template.getParentNode().insertBefore(result, template);
// TODO: clients that need to update data that is initially available,
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/AbstractTagHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/AbstractTagHandler.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/AbstractTagHandler.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/AbstractTagHandler.java
Wed Mar 18 23:05:16 2009
@@ -19,6 +19,7 @@
package org.apache.shindig.gadgets.templates;
import org.apache.shindig.gadgets.parse.nekohtml.NekoSerializer;
+import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -50,12 +51,19 @@
public String getNamespaceUri() {
return namespaceUri;
}
-
+
protected final <T> T getValueFromTag(Element tag, String name,
TemplateProcessor processor, Class<T> type) {
return processor.evaluate(tag.getAttribute(name), type, null);
}
+ protected final DocumentFragment processChildren(Element tag,
+ TemplateProcessor processor) {
+ DocumentFragment fragment =
tag.getOwnerDocument().createDocumentFragment();
+ processor.processChildNodes(fragment, tag);
+ return fragment;
+ }
+
/**
* Create a text node with proper escaping.
*/
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessor.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessor.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessor.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessor.java
Wed Mar 18 23:05:16 2009
@@ -70,17 +70,16 @@
public static final String ATTRIBUTE_VAR = "var";
private final Expressions expressions;
- private final TagRegistry registry;
// Reused buffer for creating template output
private final StringBuilder outputBuffer;
+ private TagRegistry registry;
private TemplateContext templateContext;
private ELContext elContext;
@Inject
- public DefaultTemplateProcessor(Expressions expressions, TagRegistry
registry) {
+ public DefaultTemplateProcessor(Expressions expressions) {
this.expressions = expressions;
- this.registry = registry;
outputBuffer = new StringBuilder();
}
@@ -95,12 +94,14 @@
* @return a document fragment with the resolved content
*/
public DocumentFragment processTemplate(Element template,
- TemplateContext templateContext, ELResolver globals) {
+ TemplateContext templateContext, ELResolver globals, TagRegistry
registry) {
+ this.registry = registry;
this.templateContext = templateContext;
this.elContext = expressions.newELContext(globals,
new GadgetELResolver(templateContext.getGadgetContext()),
- new TemplateELResolver(templateContext));
+ new TemplateELResolver(templateContext),
+ new ElementELResolver());
DocumentFragment result =
template.getOwnerDocument().createDocumentFragment();
processChildNodes(result, template);
@@ -234,6 +235,10 @@
*/
public void processRepeat(Node result, Element element, Iterable<?> dataList,
Runnable onEachLoop) {
+ if (dataList == null) {
+ return;
+ }
+
// Compute list size
int size = Iterables.size(dataList);
@@ -306,18 +311,15 @@
}
if (handler != null) {
- // TODO: We are passing in an element with all special attributes intact.
- // This may be problematic. Perhaps doing a deep clone and stripping them
- // would work better.
handler.process(result, element, this);
} else {
Element resultNode = (Element) element.cloneNode(false);
clearSpecialAttributes(resultNode);
processAttributes(resultNode);
- result.appendChild(resultNode);
-
processChildNodes(resultNode, element);
+ result.appendChild(resultNode);
}
+
}
private void clearSpecialAttributes(Element element) {
@@ -328,13 +330,15 @@
}
/**
- * Process expressions on attributes
+ * Process expressions on attributes. For custom tags, in addition to
+ * processing attributes,
+ * @param element The Element to process attributes on
*/
private void processAttributes(Element element) {
NamedNodeMap attributes = element.getAttributes();
for (int i = 0; i < attributes.getLength(); i++) {
Attr attribute = (Attr) attributes.item(i);
- attribute.setNodeValue(evaluate(attribute.getValue(), String.class,
null));
+ attribute.setNodeValue(evaluate(attribute.getValue(), String.class,
null));
}
}
@@ -424,6 +428,6 @@
return ImmutableList.of(json);
}
- return null;
+ return ImmutableList.of(value);
}
}
\ No newline at end of file
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java?rev=755772&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java
(added)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java
Wed Mar 18 23:05:16 2009
@@ -0,0 +1,138 @@
+/*
+ * 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.templates;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.beans.FeatureDescriptor;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+
+import com.google.common.collect.Lists;
+
+/**
+ * ELResolver that processes DOM elements.
+ */
+class ElementELResolver extends ELResolver {
+ /**
+ * A wrapper for a DOM Element that overrides toString().
+ * TODO: remove with JUEL 2.1.1.
+ */
+ public static class ElementWrapper {
+ public final Element element;
+
+ public ElementWrapper(Element element) {
+ this.element = element;
+ }
+
+ @Override
+ public String toString() {
+ return element.getTextContent();
+ }
+ }
+
+ @Override
+ public Class<?> getCommonPropertyType(ELContext context, Object base) {
+ if (base instanceof ElementWrapper) {
+ 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) {
+ Object value = getValue(context, base, property);
+ return value == null ? null : value.getClass();
+ }
+
+ @Override
+ public Object getValue(ELContext context, Object base, Object property) {
+ if (!(base instanceof ElementWrapper)) {
+ return null;
+ }
+
+ context.setPropertyResolved(true);
+ Element element = ((ElementWrapper) base).element;
+ String propertyString = property.toString();
+
+ // See if there is an Object property.
+ Object data = element.getUserData(propertyString);
+ if (data != null) {
+ return data;
+ }
+
+ // Next, check for an attribute.
+ Attr attribute = element.getAttributeNode(propertyString);
+ if (attribute != null) {
+ return attribute.getValue();
+ }
+
+ // Finally, look for child nodes with matching local names.
+ List<ElementWrapper> childElements = null;
+ for (Node child = element.getFirstChild(); child != null; child =
child.getNextSibling()) {
+ if (!(child instanceof Element)) {
+ continue;
+ }
+
+ Element childElement = (Element) child;
+ if (!propertyString.equals(childElement.getLocalName())) {
+ continue;
+ }
+
+ if (childElements == null) {
+ childElements = Lists.newArrayListWithCapacity(2);
+ }
+
+ childElements.add(new ElementWrapper(childElement));
+ }
+
+ if (childElements == null) {
+ return null;
+ } else if (childElements.size() == 1) {
+ return childElements.get(0);
+ } else {
+ return childElements;
+ }
+ }
+
+ @Override
+ public boolean isReadOnly(ELContext context, Object base, Object property) {
+ if (base instanceof ElementWrapper) {
+ context.setPropertyResolved(true);
+ }
+ return true;
+ }
+
+ @Override
+ public void setValue(ELContext context, Object base, Object property,
+ Object value) {
+ }
+
+}
Propchange:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/ElementELResolver.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java?rev=755772&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java
(added)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java
Wed Mar 18 23:05:16 2009
@@ -0,0 +1,81 @@
+/*
+ * 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.templates;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.inject.Inject;
+import org.apache.shindig.gadgets.templates.ElementELResolver.ElementWrapper;
+
+/**
+ * Tag Handler for <os:Render/> tag.
+ */
+public class RenderTagHandler extends AbstractTagHandler {
+
+ public static final String DEFAULT_NAME = "Render";
+
+ public static final String ATTR_CONTENT = "content";
+
+ @Inject
+ public RenderTagHandler() {
+ super(TagHandler.OPENSOCIAL_NAMESPACE, DEFAULT_NAME);
+ }
+
+ public void process(Node result, Element tag, TemplateProcessor processor) {
+ Map<String, Object> myMap = processor.getTemplateContext().getMy();
+ if (myMap == null) {
+ return;
+ }
+
+ String content = tag.getAttribute(ATTR_CONTENT);
+ // No @content specified - move it all.
+ if ("".equals(content)) {
+ Node root = processor.getTemplateContext().getTemplateRoot();
+ if (root != null) {
+ for (Node child = root.getFirstChild(); child != null; child =
child.getNextSibling()) {
+ result.appendChild(child.cloneNode(true));
+ }
+ }
+ } else {
+ Object value = myMap.get(content);
+ // TODO: for non-Elements, output errors
+ if (value instanceof ElementWrapper) {
+ copyChildren((ElementWrapper) value, result);
+ } else if (value instanceof List) {
+ List<?> children = (List<?>) value;
+ for (Object probablyAnElement : children) {
+ if (probablyAnElement instanceof ElementWrapper) {
+ copyChildren((ElementWrapper) probablyAnElement, result);
+ }
+ }
+ }
+ }
+ }
+
+ private void copyChildren(ElementWrapper fromWrapper, Node to) {
+ Element from = fromWrapper.element;
+ for (Node child = from.getFirstChild(); child != null; child =
child.getNextSibling()) {
+ to.appendChild(child.cloneNode(true));
+ }
+ }
+}
Propchange:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/RenderTagHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TagRegistry.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TagRegistry.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TagRegistry.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TagRegistry.java
Wed Mar 18 23:05:16 2009
@@ -20,34 +20,68 @@
import org.w3c.dom.Element;
-import com.google.common.collect.Maps;
-import com.google.inject.Inject;
-
import java.util.Map;
import java.util.Set;
+import com.google.common.collect.Maps;
+import com.google.inject.Inject;
+
/**
* A registry of custom tag handlers, keyed by a combination of namespace URL
* and tag name.
*/
public class TagRegistry {
+ private final TagRegistry parentRegistry;
private final Map<NSName, TagHandler> handlers = Maps.newHashMap();
@Inject
public TagRegistry(Set<TagHandler> handlers) {
- for (TagHandler handler : handlers) {
- this.handlers.put(new NSName(handler.getNamespaceUri(),
- handler.getTagName()), handler);
+ this(handlers, null);
+ }
+
+ /**
+ * Create a new TagRegistry containing all the existing handlers, with
+ * new tags added. The handlers from the existing registry will take
+ * precedence over any newly registered tags.
+ */
+ public TagRegistry addHandlers(Set<? extends TagHandler> handlers) {
+ if (handlers.isEmpty()) {
+ return this;
}
+
+ return new TagRegistry(handlers, this);
}
+ private TagRegistry(Set<? extends TagHandler> handlers, TagRegistry
parentRegistry) {
+ this.parentRegistry = parentRegistry;
+ for (TagHandler handler : handlers) {
+ this.handlers.put(new NSName(handler.getNamespaceUri(),
handler.getTagName()), handler);
+ }
+ }
+
+ /**
+ * Look up a tag handler for an element.
+ */
public TagHandler getHandlerFor(Element element) {
if (element.getNamespaceURI() == null) {
return null;
}
- TagHandler handler = handlers.get(new NSName(element.getNamespaceURI(),
- element.getLocalName()));
+
+ return getHandlerFor(new NSName(element.getNamespaceURI(),
element.getLocalName()));
+ }
+
+ private TagHandler getHandlerFor(NSName name) {
+ // Check the parent registry first; built-in tags cannot be overridden
+ TagHandler handler = null;
+ if (parentRegistry != null) {
+ handler = parentRegistry.getHandlerFor(name);
+ }
+
+ if (handler == null) {
+ handler = handlers.get(name);
+ }
+
return handler;
}
@@ -81,8 +115,6 @@
@Override
public int hashCode() {
return hash;
- }
-
+ }
}
-
}
Added:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java?rev=755772&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java
(added)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java
Wed Mar 18 23:05:16 2009
@@ -0,0 +1,114 @@
+/*
+ * 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.templates;
+
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.List;
+import java.util.Map;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import org.apache.shindig.gadgets.templates.ElementELResolver.ElementWrapper;
+
+/**
+ * TagHandler implemented by an declarative XML definition.
+ */
+public class TemplateBasedTagHandler extends AbstractTagHandler {
+
+ private final Element templateDefinition;
+
+ public TemplateBasedTagHandler(Element templateDefinition, String
namespaceUri, String tagName) {
+ super(namespaceUri, tagName);
+ this.templateDefinition = templateDefinition;
+ }
+
+ public void process(Node result, Element tagInstance, TemplateProcessor
processor) {
+ // Process the children of the tag
+ DocumentFragment processedContent = processChildren(tagInstance,
processor);
+
+ // Save the old values of "My", "Cur", and the template root element,
+ // and update each
+ Map<String, Object> oldMy = processor.getTemplateContext().setMy(
+ computeMy(tagInstance, processedContent, processor));
+ Object oldCur = processor.getTemplateContext().setCur(null);
+ Node oldTemplateRoot =
processor.getTemplateContext().setTemplateRoot(processedContent);
+
+ // Process the template content given the new EL state
+ processor.processChildNodes(result, templateDefinition);
+
+ // And restore the template context
+ processor.getTemplateContext().setMy(oldMy);
+ processor.getTemplateContext().setCur(oldCur);
+ processor.getTemplateContext().setTemplateRoot(oldTemplateRoot);
+ }
+
+ /**
+ * Compute the value of ${My} for this tag execution.
+ */
+ private Map<String, Object> computeMy(Element tagInstance, Node
processedContent,
+ TemplateProcessor processor) {
+ Map<String, Object> myMap = Maps.newHashMap();
+
+ NodeList children = processedContent.getChildNodes();
+
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ if (child instanceof Element) {
+ Element el = (Element) child;
+ String name = el.getLocalName();
+ // TODO: why??? There should always be a local name.
+ if (name == null) {
+ name = el.getNodeName();
+ }
+
+ ElementWrapper wrapper = new ElementWrapper(el);
+ Object previous = myMap.get(name);
+ if (previous == null) {
+ myMap.put(name, wrapper);
+ } else if (previous instanceof ElementWrapper) {
+ List<ElementWrapper> bucket =
Lists.newArrayListWithCapacity(children.getLength());
+ bucket.add((ElementWrapper) previous);
+ bucket.add(wrapper);
+ myMap.put(name, bucket);
+ } else {
+ // Must be a List<ElementWrapper>
+ @SuppressWarnings("unchecked")
+ List<ElementWrapper> bucket = (List<ElementWrapper>) previous;
+ bucket.add(wrapper);
+ }
+ }
+ }
+
+ NamedNodeMap atts = tagInstance.getAttributes();
+ for (int i = 0; i < atts.getLength(); i++) {
+ String name = atts.item(i).getNodeName();
+ // Overwrite any pre-existing values, as attributes take
+ // precedence over elements. This is wasteful if there are attributes
+ // and elements with the same name, but that should be very rare
+ myMap.put(name, getValueFromTag(tagInstance, name, processor,
Object.class));
+ }
+
+ return myMap;
+ }
+}
Propchange:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateContext.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateContext.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateContext.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateContext.java
Wed Mar 18 23:05:16 2009
@@ -20,6 +20,7 @@
import org.apache.shindig.gadgets.GadgetContext;
import org.json.JSONObject;
+import org.w3c.dom.Node;
import java.util.Map;
@@ -35,7 +36,9 @@
private Object cur = null;
// TODO: support unique Id
private Map<String, ? extends Object> context = ImmutableMap.of();
-
+ private Map<String, Object> myMap = null;
+ private Node templateRoot;
+
public TemplateContext(GadgetContext gadgetContext, Map<String, JSONObject>
top) {
this.gadgetContext = gadgetContext;
this.top = top;
@@ -65,7 +68,27 @@
return oldContext;
}
+ public Map<String, Object> setMy(Map<String, Object> myMap) {
+ Map<String, Object> oldMy = this.myMap;
+ this.myMap = myMap;
+ return oldMy;
+ }
+
+ public Map<String, Object> getMy() {
+ return myMap;
+ }
+
public GadgetContext getGadgetContext() {
return gadgetContext;
}
+
+ public Node setTemplateRoot(Node root) {
+ Node oldRoot = this.templateRoot;
+ this.templateRoot = root;
+ return oldRoot;
+ }
+
+ public Node getTemplateRoot() {
+ return this.templateRoot;
+ }
}
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateELResolver.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateELResolver.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateELResolver.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateELResolver.java
Wed Mar 18 23:05:16 2009
@@ -41,9 +41,10 @@
public static final String PROPERTY_TOP = "Top";
public static final String PROPERTY_CONTEXT = "Context";
public static final String PROPERTY_CUR = "Cur";
+ public static final String PROPERTY_MY = "My";
private static final Set<String> TOP_LEVEL_PROPERTIES =
- ImmutableSet.of(PROPERTY_TOP, PROPERTY_CONTEXT, PROPERTY_CUR);
+ ImmutableSet.of(PROPERTY_TOP, PROPERTY_CONTEXT, PROPERTY_CUR, PROPERTY_MY);
private final TemplateContext templateContext;
@@ -81,6 +82,8 @@
return templateContext.getTop();
} else if (PROPERTY_CONTEXT.equals(property)) {
return templateContext.getContext();
+ } else if (PROPERTY_MY.equals(property)) {
+ return templateContext.getMy();
} else {
return templateContext.getCur();
}
@@ -99,25 +102,27 @@
Object cur = templateContext.getCur();
// Resolve through "cur" as if it were a value - if
"isPropertyResolved()"
// is true, it was handled
- Object value = context.getELResolver().getValue(context, cur, property);
- if (context.isPropertyResolved()) {
- if (value != null) {
- return value;
- } else {
- context.setPropertyResolved(false);
+ if (cur != null) {
+ Object value = context.getELResolver().getValue(context, cur,
property);
+ if (context.isPropertyResolved()) {
+ if (value != null) {
+ return value;
+ } else {
+ context.setPropertyResolved(false);
+ }
}
}
// Check current scope variables next.
- Map<String, ? extends Object> scope = templateContext.getContext();
- if (scope.containsKey(property)) {
+ Map<String, ? extends Object> scope = templateContext.getMy();
+ if (scope != null && scope.containsKey(property)) {
context.setPropertyResolved(true);
return scope.get(property);
}
// Look at Top context last.
scope = templateContext.getTop();
- if (scope.containsKey(property)) {
+ if (scope != null && scope.containsKey(property)) {
context.setPropertyResolved(true);
return scope.get(property);
}
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateModule.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateModule.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateModule.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateModule.java
Wed Mar 18 23:05:16 2009
@@ -43,9 +43,10 @@
@Inject
public TagHandlersProvider(HtmlTagHandler htmlHandler, NameTagHandler
nameHandler,
- IfTagHandler ifHandler, RepeatTagHandler repeatHandler) {
+ IfTagHandler ifHandler, RepeatTagHandler repeatHandler,
+ RenderTagHandler renderHandler) {
handlers = ImmutableSet.of((TagHandler) htmlHandler, nameHandler,
ifHandler,
- repeatHandler);
+ repeatHandler, renderHandler);
}
public Set<TagHandler> get() {
Modified:
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateProcessor.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateProcessor.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateProcessor.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/templates/TemplateProcessor.java
Wed Mar 18 23:05:16 2009
@@ -43,7 +43,7 @@
* @return a document fragment with the resolved content
*/
DocumentFragment processTemplate(Element template,
- TemplateContext templateContext, ELResolver globals);
+ TemplateContext templateContext, ELResolver globals, TagRegistry
registry);
/**
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/rewrite/TemplateRewriterTest.java
Wed Mar 18 23:05:16 2009
@@ -38,11 +38,11 @@
import org.junit.Before;
import org.junit.Test;
+import java.util.Set;
+
import com.google.common.collect.ImmutableSet;
import com.google.inject.Provider;
-import java.util.Set;
-
/**
* Tests for TemplateRewriter
*/
@@ -71,21 +71,21 @@
"<script type='text/os-template' name='myTemplate'>Hello,
${user.name}</script>";
private static final String CONTENT_WITH_TAG =
- "<script type='text/os-template' tag='foo:Bar'>Hello,
${user.name}</script>";
+ "<script type='text/os-template' xmlns:foo='#foo' tag='foo:Bar'>Hello,
${user.name}</script>";
@Before
public void setUp() {
+ Set<TagHandler> handlers = ImmutableSet.of();
rewriter = new TemplateRewriter(
new Provider<TemplateProcessor>() {
public TemplateProcessor get() {
- Set<TagHandler> handlers = ImmutableSet.of();
- return new DefaultTemplateProcessor(new Expressions(),
- new TagRegistry(handlers));
+ return new DefaultTemplateProcessor(new Expressions());
}
},
new FakeMessageBundleFactory(),
- new Expressions());
+ new Expressions(),
+ new TagRegistry(handlers));
}
@Test
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/DefaultTemplateProcessorTest.java
Wed Mar 18 23:05:16 2009
@@ -19,6 +19,7 @@
package org.apache.shindig.gadgets.templates;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
import org.apache.shindig.expressions.Expressions;
@@ -73,7 +74,7 @@
Set<TagHandler> handlers = ImmutableSet.of((TagHandler) new
TestTagHandler());
registry = new TagRegistry(handlers);
- processor = new DefaultTemplateProcessor(expressions, registry);
+ processor = new DefaultTemplateProcessor(expressions);
resolver = new RootELResolver();
parser = new SocialMarkupHtmlParser(new
ParseModule.DOMImplementationProvider().get());
context = new TemplateContext(new GadgetContext(), variables);
@@ -155,7 +156,7 @@
@Test
public void testCustomTag() throws Exception {
- String output = executeTemplate("<test:Foo text='${foo.title}'/>",
+ String output = executeTemplate("<test:Foo text='${foo.title}'
data='${user}'/>",
"xmlns:test='" + TEST_NS + "'");
assertEquals("<b>BAR</b>", output);
}
@@ -166,7 +167,7 @@
private String executeTemplate(String markup, String extra) throws Exception
{
Element template = prepareTemplate(markup, extra);
- DocumentFragment result = processor.processTemplate(template, context,
resolver);
+ DocumentFragment result = processor.processTemplate(template, context,
resolver, registry);
return serialize(result);
}
@@ -191,9 +192,10 @@
}
/**
- * A dummy custom tag that looks for the @text attribute, uppercases it and
- * encloses it in a <b> tag. <code><test:Foo text="bar"/></code>
- * turns into <code><b>BAR</b></code>
+ * A dummy custom tag.
+ * Expects a @text attribute equal to "bar", and a @data attribute that
+ * evaluates to a JSONObject with an id property equal to "101".
+ * If these conditions are met, returns <code><b>BAR</b></code>
*/
private static class TestTagHandler extends AbstractTagHandler {
@@ -201,9 +203,13 @@
super(TEST_NS, "Foo");
}
- public void process(Node result, Element tag, TemplateProcessor processor)
{
+ public void process(Node result, Element tag, TemplateProcessor processor)
{
+ Object data = getValueFromTag(tag, "data", processor, Object.class);
+ assertTrue(data instanceof JSONObject);
+ assertEquals("101", ((JSONObject) data).optString("id"));
+
String text = getValueFromTag(tag, "text", processor, String.class);
- text = (text == null) ? "" : text.toUpperCase();
+ text = text.toUpperCase();
Document doc = result.getOwnerDocument();
Element b = doc.createElement("b");
b.appendChild(doc.createTextNode(text));
Modified:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/FakeTemplateProcessor.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/FakeTemplateProcessor.java?rev=755772&r1=755771&r2=755772&view=diff
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/FakeTemplateProcessor.java
(original)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/FakeTemplateProcessor.java
Wed Mar 18 23:05:16 2009
@@ -53,7 +53,7 @@
}
public DocumentFragment processTemplate(Element template,
- TemplateContext templateContext, ELResolver globals) {
+ TemplateContext templateContext, ELResolver globals, TagRegistry
registry) {
throw new UnsupportedOperationException();
}
Added:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java?rev=755772&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java
Wed Mar 18 23:05:16 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.templates;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.shindig.expressions.Expressions;
+import org.apache.shindig.expressions.RootELResolver;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.parse.ParseModule;
+import org.apache.shindig.gadgets.parse.nekohtml.NekoSerializer;
+import org.apache.shindig.gadgets.parse.nekohtml.SocialMarkupHtmlParser;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+
+import javax.el.ELResolver;
+
+public class RenderTagHandlerTest {
+
+ private Expressions expressions;
+
+ private TemplateContext context;
+ private DefaultTemplateProcessor processor;
+ private Map<String, JSONObject> variables;
+ private ELResolver resolver;
+ private TagRegistry registry;
+
+ private SocialMarkupHtmlParser parser;
+
+ private static final String TEST_NS = "http://example.com";
+
+ @Before
+ public void setUp() throws Exception {
+ expressions = new Expressions();
+ variables = Maps.newHashMap();
+ Set<TagHandler> handlers = ImmutableSet.of((TagHandler) new
RenderTagHandler());
+ registry = new TagRegistry(handlers);
+
+ processor = new DefaultTemplateProcessor(expressions);
+ resolver = new RootELResolver();
+ parser = new SocialMarkupHtmlParser(new
ParseModule.DOMImplementationProvider().get());
+ context = new TemplateContext(new GadgetContext(), variables);
+
+ addVariable("foo", new JSONObject("{ title: 'bar' }"));
+ }
+
+ @Test
+ public void renderAllChildren() throws Exception {
+ runTest("Bar",
+ "[<os:Render/>]",
+ "<foo:Bar>Hello</foo:Bar>", "[Hello]");
+ }
+
+ @Test
+ public void renderSingleChildren() throws Exception {
+ runTest("Panel",
+ "<os:Render content='header'/> <os:Render content='footer'/>",
+ "<foo:Panel><footer>Second</footer><header>First</header></foo:Panel>",
+ "First Second");
+ }
+
+ private void runTest(String tagName, String tagMarkup, String
templateMarkup,
+ String expectedResult) throws GadgetException, IOException {
+ Element templateDef = parseTemplate(templateMarkup);
+ Element tagInstance = parseTemplate(tagMarkup);
+
+ templateDef.getOwnerDocument().adoptNode(tagInstance);
+ TemplateBasedTagHandler tagHandler = new
TemplateBasedTagHandler(tagInstance, TEST_NS, tagName);
+ TagRegistry reg = registry.addHandlers(ImmutableSet.of(tagHandler));
+
+ DocumentFragment result = processor.processTemplate(templateDef, context,
resolver, reg);
+ String output = serialize(result);
+ assertEquals(expectedResult, output);
+ }
+
+ private Element parseTemplate(String markup) throws GadgetException {
+ String content = "<script type=\"text/os-template\" xmlns:foo=\"" +
TEST_NS +
+ "\" xmlns:os=\"" + TagHandler.OPENSOCIAL_NAMESPACE + "\">" + markup +
"</script>";
+ Document document = parser.parseDom(content);
+ return (Element) document.getElementsByTagName("script").item(0);
+ }
+
+ private String serialize(Node node) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ NekoSerializer.serialize(child, sb);
+ }
+ return sb.toString();
+ }
+
+ private void addVariable(String key, JSONObject value) {
+ variables.put(key, value);
+ }
+
+}
Propchange:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/RenderTagHandlerTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Added:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java
URL:
http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java?rev=755772&view=auto
==============================================================================
---
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java
(added)
+++
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java
Wed Mar 18 23:05:16 2009
@@ -0,0 +1,179 @@
+/*
+ * 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.templates;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.shindig.common.PropertiesModule;
+import org.apache.shindig.expressions.RootELResolver;
+import org.apache.shindig.gadgets.GadgetContext;
+import org.apache.shindig.gadgets.GadgetException;
+import org.apache.shindig.gadgets.parse.GadgetHtmlParser;
+import org.apache.shindig.gadgets.parse.ParseModule;
+import org.apache.shindig.gadgets.parse.nekohtml.NekoSerializer;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.IOException;
+
+import javax.el.ELResolver;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+/**
+ * Tests the behavior of template-based tag handlers.
+ */
+public class TemplateBasedTagHandlerTest {
+
+ private TemplateContext context;
+ private TemplateProcessor processor;
+ private final ELResolver resolver = new RootELResolver();
+ private GadgetHtmlParser parser;
+
+ private static final String TEST_NS = "http://example.com";
+
+ @Before
+ public void setUp() throws Exception {
+ Injector injector = Guice.createInjector(new ParseModule(), new
TemplateModule(),
+ new PropertiesModule());
+ parser = injector.getInstance(GadgetHtmlParser.class);
+ processor = injector.getInstance(TemplateProcessor.class);
+ context = new TemplateContext(new GadgetContext(), null);
+ }
+
+ @Test
+ public void attributeInMy() throws Exception {
+ // Verify attribute EL retrieval
+ runTest("Bar",
+ "${My.attr}",
+ "<foo:Bar attr='Hello'/>", "Hello");
+ }
+
+ @Test
+ public void elementContentInMy() throws Exception {
+ // Verify element content EL retrieval
+ runTest("Bar",
+ "${My.element}",
+ "<foo:Bar><foo:element>Hello</foo:element></foo:Bar>", "Hello");
+ }
+
+ @Test
+ public void attrTakesPrecedenceInMy() throws Exception {
+ // Verify an attribute takes precedence over an element
+ runTest("Bar",
+ "${My.attr}",
+ "<foo:Bar attr='Hello'><foo:attr>Goodbye</foo:attr></foo:Bar>",
"Hello");
+ }
+
+ @Test
+ public void elementAttributeInMy() throws Exception {
+ // Verify an attribute of an element is visible
+ runTest("Bar",
+ "${My.element.text}",
+ "<foo:Bar><foo:element text='Hello'/></foo:Bar>", "Hello");
+ }
+
+ @Test
+ public void descendantElementInMy() throws Exception {
+ // Verify the descendant of an element is visible
+ runTest("Bar",
+ "${My.element.child}",
+
"<foo:Bar><foo:element><foo:child>Hello</foo:child></foo:element></foo:Bar>",
"Hello");
+ }
+
+ @Test
+ public void descendantElementNotFoundIfNotFullReference() throws Exception {
+ // Verify the descendant element isn't visible unless directly referenced
+ runTest("Bar",
+ "${My.child}",
+
"<foo:Bar><foo:element><foo:child>Hello</foo:child></foo:element></foo:Bar>",
"");
+ }
+
+ @Test
+ public void missingElementPropertyIsNull() throws Exception {
+ // Verify the descendant element isn't visible unless directly referenced
+ runTest("Bar",
+ "${My.element.foo == null}",
+ "<foo:Bar><foo:element>Hello/foo:element></foo:Bar>", "true");
+ }
+
+ @Test
+ @Ignore("This currently returns [Hello,Goodbye]. Check the spec, and
consider changing the spec.")
+ public void multipleElementContentInMy() throws Exception {
+ // Verify element content EL retrieval is concatenation for multiple
elements
+ runTest("Bar",
+ "${My.element}",
+
"<foo:Bar><foo:element>Hello</foo:element><foo:element>Goodbye</foo:element></foo:Bar>",
"HelloGoodbye");
+ }
+
+ @Test
+ public void elementListRepeat() throws Exception {
+ // Verify a list of elements can be repeated over
+ runTest("Bar",
+ "<os:Repeat expression='${My.element}'>${text}</os:Repeat>",
+ "<foo:Bar><foo:element text='Hello'/><foo:element
text='Goodbye'/></foo:Bar>", "HelloGoodbye");
+ }
+
+ @Test
+ public void singleElementRepeat() throws Exception {
+ // Verify a single element can be "repeated" over
+ runTest("Bar",
+ "<os:Repeat expression='${My.element}'>${text}</os:Repeat>",
+ "<foo:Bar><foo:element text='Hello'/></foo:Bar>", "Hello");
+ }
+
+ private void runTest(String tagName, String tagMarkup, String
templateMarkup,
+ String expectedResult) throws GadgetException, IOException {
+ Element templateDef = parseTemplate(templateMarkup);
+ Element tagInstance = parseTemplate(tagMarkup);
+
+ templateDef.getOwnerDocument().adoptNode(tagInstance);
+ TagHandler tagHandler = new TemplateBasedTagHandler(tagInstance, TEST_NS,
tagName);
+ TagRegistry reg = new TagRegistry(ImmutableSet.of(tagHandler, new
RepeatTagHandler()));
+
+ DocumentFragment result = processor.processTemplate(templateDef, context,
resolver, reg);
+ String output = serialize(result);
+ assertEquals(expectedResult, output);
+ }
+
+ private Element parseTemplate(String markup) throws GadgetException {
+ String content = "<script type=\"text/os-template\" xmlns:foo=\"" +
TEST_NS +
+ "\" xmlns:os=\"" + TagHandler.OPENSOCIAL_NAMESPACE + "\">" + markup +
"</script>";
+ Document document = parser.parseDom(content);
+ return (Element) document.getElementsByTagName("script").item(0);
+ }
+
+ private String serialize(Node node) throws IOException {
+ StringBuilder sb = new StringBuilder();
+ NodeList children = node.getChildNodes();
+ for (int i = 0; i < children.getLength(); i++) {
+ Node child = children.item(i);
+ NekoSerializer.serialize(child, sb);
+ }
+ return sb.toString();
+ }
+}
Propchange:
incubator/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/templates/TemplateBasedTagHandlerTest.java
------------------------------------------------------------------------------
svn:eol-style = native