You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2014/11/14 15:05:03 UTC
svn commit: r1639641 [10/15] - in /sling/trunk/contrib/scripting/sightly: ./
engine/ engine/src/ engine/src/main/ engine/src/main/antlr4/
engine/src/main/antlr4/org/ engine/src/main/antlr4/org/apache/
engine/src/main/antlr4/org/apache/sling/ engine/src...
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/ScriptUseProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/ScriptUseProvider.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/ScriptUseProvider.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/ScriptUseProvider.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,115 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.engine.extension.use;
+
+import javax.script.Bindings;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScript;
+import org.apache.sling.api.scripting.SlingScriptHelper;
+import org.apache.sling.scripting.sightly.api.ProviderOutcome;
+import org.apache.sling.scripting.sightly.api.UseProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.sling.scripting.sightly.api.ProviderOutcome;
+import org.apache.sling.scripting.sightly.api.RenderContext;
+import org.apache.sling.scripting.sightly.api.SightlyUseException;
+import org.apache.sling.scripting.sightly.api.UseProvider;
+import org.apache.sling.scripting.sightly.api.UseProviderComponent;
+
+/**
+ * Use provider that interprets the identifier as a script path, and runs the respective script using a script engine that matches the
+ * script extension.
+ *
+ * This provider returns a non-failure outcome only if the evaluated script actually returns something. For more details check the
+ * implementation of the {@link SlingScript#eval(SlingBindings)} method for the available script engines from your platform.
+ */
+@Component
+@Service(UseProvider.class)
+@Property(name = UseProviderComponent.PRIORITY, intValue = 15)
+public class ScriptUseProvider extends UseProviderComponent {
+
+ private static final Logger log = LoggerFactory.getLogger(ScriptUseProvider.class);
+
+ @Reference
+ private ResourceResolverFactory rrf = null;
+
+ @Override
+ public ProviderOutcome provide(String scriptName, RenderContext renderContext, Bindings arguments) {
+ Bindings globalBindings = renderContext.getBindings();
+ Bindings bindings = merge(globalBindings, arguments);
+ String extension = scriptExtension(scriptName);
+ if (extension == null) {
+ return ProviderOutcome.failure();
+ }
+ SlingScriptHelper sling = (SlingScriptHelper) bindings.get(SlingBindings.SLING);
+ ResourceResolver adminResolver = null;
+ try {
+ adminResolver = rrf.getAdministrativeResourceResolver(null);
+ if (adminResolver == null) {
+ log.warn("Cannot obtain administrative resource resolver for " + scriptName);
+ return ProviderOutcome.failure();
+ }
+ Resource scriptResource = ScriptEvalUtils.locateScriptResource(adminResolver, sling, scriptName);
+ if (scriptResource == null) {
+ log.debug("Path does not match an existing resource: {}", scriptName);
+ return ProviderOutcome.failure();
+ }
+ return evalScript(scriptResource, bindings);
+ } catch (LoginException e) {
+ throw new SightlyUseException(e);
+ } finally {
+ if (adminResolver != null) {
+ adminResolver.close();
+ }
+ }
+
+ }
+
+ private ProviderOutcome evalScript(Resource scriptResource, Bindings bindings) {
+ SlingScript slingScript = scriptResource.adaptTo(SlingScript.class);
+ if (slingScript == null) {
+ return ProviderOutcome.failure();
+ }
+ SlingBindings slingBindings = new SlingBindings();
+ slingBindings.putAll(bindings);
+ Object scriptEval = slingScript.eval(slingBindings);
+ return ProviderOutcome.notNullOrFailure(scriptEval);
+ }
+
+ private String scriptExtension(String path) {
+ String extension = StringUtils.substringAfterLast(path, ".");
+ if (StringUtils.isEmpty(extension)) {
+ extension = null;
+ }
+ return extension;
+ }
+
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/UseRuntimeExtension.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/UseRuntimeExtension.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/UseRuntimeExtension.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/extension/use/UseRuntimeExtension.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.engine.extension.use;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import javax.script.Bindings;
+import javax.script.SimpleBindings;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+
+import org.apache.sling.scripting.sightly.api.RuntimeExtensionComponent;
+import org.apache.sling.scripting.sightly.api.ExtensionInstance;
+import org.apache.sling.scripting.sightly.api.ProviderOutcome;
+import org.apache.sling.scripting.sightly.api.RenderContext;
+import org.apache.sling.scripting.sightly.api.RuntimeExtension;
+import org.apache.sling.scripting.sightly.api.RuntimeExtensionException;
+import org.apache.sling.scripting.sightly.api.SightlyUseException;
+import org.apache.sling.scripting.sightly.api.UseProvider;
+import org.apache.sling.scripting.sightly.plugin.UsePlugin;
+
+/**
+ * Runtime extension for the USE plugin
+ */
+@Component
+@Service(RuntimeExtension.class)
+@Properties(
+ @Property(name = RuntimeExtensionComponent.SCR_PROP_NAME, value = UsePlugin.FUNCTION_NAME)
+)
+@Reference(
+ policy = ReferencePolicy.DYNAMIC,
+ referenceInterface = UseProvider.class,
+ name = "useProvider",
+ cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE
+)
+public class UseRuntimeExtension extends RuntimeExtensionComponent {
+
+ private volatile List<UseProvider> providers = Collections.emptyList();
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public ExtensionInstance provide(final RenderContext renderContext) {
+ return new ExtensionInstance() {
+
+ @Override
+ public Object call(Object... arguments) {
+ if (arguments.length != 2) {
+ throw new RuntimeExtensionException("Use extension requires two arguments");
+ }
+ String identifier = renderContext.getObjectModel().coerceToString(arguments[0]);
+ if (StringUtils.isEmpty(identifier)) {
+ return null;
+ }
+ Map<String, Object> useArgumentsMap = renderContext.getObjectModel().coerceToMap(arguments[1]);
+ Bindings useArguments = new SimpleBindings(Collections.unmodifiableMap(useArgumentsMap));
+ for (UseProvider provider : providers) {
+ ProviderOutcome outcome = provider.provide(identifier, renderContext, useArguments);
+ if (outcome.isSuccess()) {
+ return outcome.getResult();
+ }
+ }
+ throw new SightlyUseException("No use provider could resolve identifier: " + identifier);
+ }
+ };
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ private void bindUseProvider(UseProvider provider) {
+ ArrayList<UseProvider> newProviders = new ArrayList<UseProvider>(providers);
+ newProviders.add(provider);
+ Collections.sort(newProviders);
+ providers = newProviders;
+ }
+
+ @SuppressWarnings("UnusedDeclaration")
+ private void unbindUseProvider(UseProvider provider) {
+ ArrayList<UseProvider> newProviders = new ArrayList<UseProvider>(providers);
+ newProviders.remove(provider);
+ Collections.sort(newProviders);
+ providers = newProviders;
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/runtime/UnitLocatorImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/runtime/UnitLocatorImpl.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/runtime/UnitLocatorImpl.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/engine/runtime/UnitLocatorImpl.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.engine.runtime;
+
+import javax.script.Bindings;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.scripting.sightly.api.RenderUnit;
+import org.apache.sling.scripting.sightly.api.ResourceResolution;
+import org.apache.sling.scripting.sightly.api.UnitLocator;
+import org.apache.sling.scripting.sightly.engine.UnitLoader;
+
+/**
+ * Implementation for unit locator
+ */
+public class UnitLocatorImpl implements UnitLocator {
+
+ private final UnitLoader unitLoader;
+ private final ResourceResolver resolver;
+ private final Bindings bindings;
+ private final Resource currentScriptResource;
+
+ public UnitLocatorImpl(UnitLoader unitLoader, ResourceResolver resourceResolver,
+ Bindings bindings, Resource currentScriptResource) {
+ this.unitLoader = unitLoader;
+ this.resolver = resourceResolver;
+ this.bindings = bindings;
+ this.currentScriptResource = currentScriptResource;
+ }
+
+ @Override
+ public RenderUnit locate(String path) {
+ Resource resource = locateResource(path);
+ if (resource == null) {
+ return null;
+ }
+ return unitLoader.createUnit(resource, bindings);
+ }
+
+ private Resource locateResource(String script) {
+ SlingHttpServletRequest request = (SlingHttpServletRequest) bindings.get(SlingBindings.REQUEST);
+ Resource resource = ResourceResolution.resolveComponentForRequest(resolver, request);
+ if (resource != null) {
+ resource = ResourceResolution.resolveComponentRelative(resolver, resource, script);
+ } else {
+ resource = ResourceResolution.resolveComponentRelative(resolver, currentScriptResource, script);
+ }
+ return resource;
+ }
+
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FilterComponent.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FilterComponent.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FilterComponent.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FilterComponent.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.filter;
+
+import java.util.Dictionary;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.osgi.service.component.ComponentContext;
+
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+
+/**
+ * Filters implemented as components.
+ */
+public abstract class FilterComponent implements Filter {
+
+ public static final String PRIORITY = "org.apache.sling.scripting.sightly.filter.priority";
+ public static final int DEFAULT_PRIORITY = 100;
+
+ private int priority = DEFAULT_PRIORITY;
+
+ @Override
+ public int priority() {
+ return priority;
+ }
+
+ @Override
+ public int compareTo(Filter o) {
+ if (this.priority < o.priority()) {
+ return -1;
+ } else if (this.priority == o.priority()) {
+ return 0;
+ }
+ return 1;
+ }
+
+ @Activate
+ protected void activate(ComponentContext componentContext) {
+ Dictionary properties = componentContext.getProperties();
+ priority = PropertiesUtil.toInteger(properties.get(PRIORITY), DEFAULT_PRIORITY);
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FormatFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FormatFilter.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FormatFilter.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/FormatFilter.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.filter;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+import org.apache.sling.scripting.sightly.api.ExtensionInstance;
+import org.apache.sling.scripting.sightly.api.RenderContext;
+import org.apache.sling.scripting.sightly.api.RuntimeExtension;
+import org.apache.sling.scripting.sightly.api.RuntimeExtensionException;
+import org.apache.sling.scripting.sightly.common.Dynamic;
+import org.apache.sling.scripting.sightly.api.ExtensionInstance;
+import org.apache.sling.scripting.sightly.common.Dynamic;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+
+/**
+ * Implementation for the format filter & runtime support.
+ */
+@Component
+@Service({Filter.class, RuntimeExtension.class})
+public class FormatFilter extends FilterComponent implements RuntimeExtension {
+
+ public static final String FORMAT_OPTION = "format";
+ public static final String FORMAT_FUNCTION = "format";
+
+ private static final Pattern PLACEHOLDER_REGEX = Pattern.compile("\\{\\d}");
+
+ @Override
+ public Expression apply(Expression expression) {
+ //todo: if the expression is a string constant, we can produce the transformation at
+ //compile time, with no need of a runtime function
+ if (!expression.containsOption(FORMAT_OPTION)) {
+ return expression;
+ }
+ ExpressionNode argNode = expression.getOption(FORMAT_OPTION);
+ ExpressionNode formattedNode = new RuntimeCall(FORMAT_FUNCTION, expression.getRoot(), argNode);
+ return expression.withNode(formattedNode).removeOptions(FORMAT_OPTION);
+ }
+
+ @Override
+ public String name() {
+ return FORMAT_FUNCTION;
+ }
+
+ @Override
+ public ExtensionInstance provide(RenderContext renderContext) {
+ final Dynamic dynamic = new Dynamic(renderContext.getObjectModel());
+
+ return new ExtensionInstance() {
+ @Override
+ public Object call(Object... arguments) {
+ if (arguments.length != 2) {
+ throw new RuntimeExtensionException("Format function must be called with two arguments");
+ }
+ String source = dynamic.coerceToString(arguments[0]);
+ Object[] params = decodeParams(arguments[1]);
+ return replace(source, params);
+ }
+
+ private Object[] decodeParams(Object paramObj) {
+ if (dynamic.isCollection(paramObj)) {
+ return dynamic.coerceToCollection(paramObj).toArray();
+ }
+ return new Object[] {paramObj};
+ }
+
+ private String replace(String source, Object[] params) {
+ Matcher matcher = PLACEHOLDER_REGEX.matcher(source);
+ StringBuilder builder = new StringBuilder();
+ int lastPos = 0;
+ boolean matched = true;
+ while (matched) {
+ matched = matcher.find();
+ if (matched) {
+ int paramIndex = placeholderIndex(matcher.group());
+ String replacement = param(params, paramIndex);
+ int matchStart = matcher.start();
+ int matchEnd = matcher.end();
+ builder.append(source, lastPos, matchStart).append(replacement);
+ lastPos = matchEnd;
+ }
+ }
+ builder.append(source, lastPos, source.length());
+ return builder.toString();
+ }
+
+ private String param(Object[] params, int index) {
+ if (index >= 0 && index < params.length) {
+ return dynamic.coerceToString(params[index]);
+ }
+ return "";
+ }
+
+ private int placeholderIndex(String placeholder) {
+ return Integer.parseInt(placeholder.substring(1, placeholder.length() - 1));
+ }
+ };
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/I18nFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/I18nFilter.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/I18nFilter.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/I18nFilter.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,68 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.filter;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.NullLiteral;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.NullLiteral;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+
+/**
+ * Filter for i18n translation
+ */
+@Component
+@Service(Filter.class)
+@Property(name = FilterComponent.PRIORITY, intValue = 90)
+public class I18nFilter extends FilterComponent {
+
+ public static final String FUNCTION = "i18nTranslation";
+
+ public static final String I18N_OPTION = "i18n";
+ public static final String HINT_OPTION = "hint";
+ public static final String LOCALE_OPTION = "locale";
+
+ @Override
+ public Expression apply(Expression expression) {
+ if (!expression.containsOption(I18N_OPTION)) {
+ return expression;
+ }
+ ExpressionNode hint = option(expression, HINT_OPTION);
+ ExpressionNode locale = option(expression, LOCALE_OPTION);
+ ExpressionNode translation = new RuntimeCall(FUNCTION, expression.getRoot(), locale, hint);
+ return expression.withNode(translation).removeOptions(HINT_OPTION, LOCALE_OPTION);
+ }
+
+ private ExpressionNode option(Expression expression, String optionName) {
+ ExpressionNode node = expression.getOption(optionName);
+ if (node == null) {
+ return NullLiteral.INSTANCE;
+ }
+ return node;
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/JoinFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/JoinFilter.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/JoinFilter.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/JoinFilter.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.filter;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+
+import org.apache.sling.scripting.sightly.api.ExtensionInstance;
+import org.apache.sling.scripting.sightly.api.RenderContext;
+import org.apache.sling.scripting.sightly.api.RuntimeExtension;
+import org.apache.sling.scripting.sightly.api.RuntimeExtensionException;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+import org.apache.sling.scripting.sightly.common.Dynamic;
+import org.apache.sling.scripting.sightly.api.ExtensionInstance;
+import org.apache.sling.scripting.sightly.api.RenderContext;
+import org.apache.sling.scripting.sightly.api.RuntimeExtension;
+import org.apache.sling.scripting.sightly.api.RuntimeExtensionException;
+import org.apache.sling.scripting.sightly.common.Dynamic;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+
+/**
+ * Filter providing support for the {@code join} option applied to arrays.
+ */
+@Component
+@Service({Filter.class, RuntimeExtension.class})
+public class JoinFilter extends FilterComponent implements RuntimeExtension {
+
+ public static final String JOIN_OPTION = "join";
+ public static final String JOIN_FUNCTION = "join";
+
+ @Override
+ public Expression apply(Expression expression) {
+ if (!expression.containsOption(JOIN_OPTION)) {
+ return expression;
+ }
+ ExpressionNode argumentNode = expression.getOption(JOIN_OPTION);
+ ExpressionNode joinResult = new RuntimeCall(JOIN_FUNCTION, expression.getRoot(), argumentNode);
+ return expression.withNode(joinResult).removeOptions(JOIN_OPTION);
+ }
+
+ @Override
+ public String name() {
+ return JOIN_FUNCTION;
+ }
+
+ @Override
+ public ExtensionInstance provide(RenderContext renderContext) {
+ final Dynamic dynamic = new Dynamic(renderContext.getObjectModel());
+
+ return new ExtensionInstance() {
+ @Override
+ public Object call(Object... arguments) {
+ if (arguments.length != 2) {
+ throw new RuntimeExtensionException("Join function must be called with two arguments.");
+ }
+ Collection<?> collection = dynamic.coerceToCollection(arguments[0]);
+ String joinString = dynamic.coerceToString(arguments[1]);
+ return join(collection, joinString);
+ }
+
+ private String join(Collection<?> collection, String joinString) {
+ StringBuilder sb = new StringBuilder();
+ Iterator<?> iterator = collection.iterator();
+ while (iterator.hasNext()) {
+ String element = dynamic.coerceToString(iterator.next());
+ sb.append(element);
+ if (iterator.hasNext()) {
+ sb.append(joinString);
+ }
+ }
+ return sb.toString();
+ }
+ };
+
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/XSSFilter.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/XSSFilter.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/XSSFilter.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/filter/XSSFilter.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.filter;
+
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+
+import org.apache.sling.scripting.sightly.compiler.Syntax;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+import org.apache.sling.scripting.sightly.compiler.api.expression.ExpressionNode;
+import org.apache.sling.scripting.sightly.compiler.api.expression.node.RuntimeCall;
+import org.apache.sling.scripting.sightly.compiler.Syntax;
+import org.apache.sling.scripting.sightly.compiler.api.Filter;
+import org.apache.sling.scripting.sightly.compiler.api.expression.Expression;
+
+/**
+ * XSS filter implementation
+ */
+@Component
+@Service(Filter.class)
+@Property(name = FilterComponent.PRIORITY, intValue = 110)
+public class XSSFilter extends FilterComponent {
+
+ public static final String FUNCTION_NAME = "xss";
+
+ @Override
+ public Expression apply(Expression expression) {
+ ExpressionNode node = expression.getRoot();
+ Map<String, ExpressionNode> options = expression.getOptions();
+ ExpressionNode context = options.get(Syntax.CONTEXT_OPTION);
+ if (context != null) {
+ return new Expression(new RuntimeCall(FUNCTION_NAME, node, context), options);
+ }
+ return expression;
+ }
+
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/MarkupUtils.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/MarkupUtils.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/MarkupUtils.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/MarkupUtils.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+
+package org.apache.sling.scripting.sightly.html;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility methods related to markup
+ */
+public class MarkupUtils {
+
+ private static final Pattern ATTRIBUTE_BLACKLIST = Pattern.compile("^(style|(on.*))$", Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Attributes which should not be generated by Sightly
+ * @param name the name of the attribute
+ * @return if the attribute is sensitive or not
+ */
+ public static boolean isSensitiveAttribute(String name) {
+ return ATTRIBUTE_BLACKLIST.matcher(name).matches();
+ }
+
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/AttributeList.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/AttributeList.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/AttributeList.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/AttributeList.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom;
+
+import java.util.Iterator;
+
+/**
+ * Contains the list of attributes inside an HTML tag.
+ */
+public interface AttributeList {
+
+ /**
+ * Return the count of attributes
+ * @return count of attributes
+ */
+ int attributeCount();
+
+ /**
+ * Return the list of attribute names
+ * @return <code>Iterator</code> iterating over the attribute names
+ */
+ Iterator<String> attributeNames();
+
+ /**
+ * Return a flag indicating whether a specified attribute exists
+ * @return <code>true</code> if the specified attribute exists,
+ * <code>false</code> otherwise
+ */
+ boolean containsAttribute(String name);
+
+ /**
+ * Return an attribute's value, given its name or <code>null</code>
+ * if the attribute cannot be found.
+ * @param name attribute name
+ * @return an attribute's value
+ */
+ String getValue(String name);
+
+ /**
+ * Return an attribute's quote character, given its name or <code>0</code>
+ * if the attribute cannot be found.
+ * @param name attribute name
+ * @return an attribute's quote character
+ */
+ char getQuoteChar(String name);
+
+ /**
+ * Return an attribute's value, already surrounded with the quotes
+ * originally in place. Returns <code>null</code> if the attribute
+ * cannot be found
+ * @param name attribute name
+ * @return an attribute's value
+ */
+ String getQuotedValue(String name);
+
+ /**
+ * Set an attribute's value. If the value is <code>null</code>, this
+ * is semantically different to a {@link #removeValue(String)}.
+ *
+ * @param name attribute name
+ * @param value attribute value
+ */
+ void setValue(String name, String value);
+
+ /**
+ * Remove an attribute's value.
+ * @param name attribute name
+ */
+ void removeValue(String name);
+
+ /**
+ * Return a flag indicating whether this object was modified.
+ * @return <code>true</code> if the object was modified
+ * <code>false</code> otherwise
+ */
+ boolean isModified();
+}
\ No newline at end of file
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/DocumentHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/DocumentHandler.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/DocumentHandler.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/DocumentHandler.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom;
+
+import java.io.IOException;
+
+
+/**
+ * Invoked by the <code>HTMLParser</code> when elements are scanned.
+ */
+public interface DocumentHandler {
+
+ /**
+ * Receive notification of unparsed character data.
+ */
+ void onCharacters(char[] ch, int off, int len) throws IOException;
+
+ void onComment(String characters) throws IOException;
+
+ /**
+ * Receive notification of the beginning of an element.
+ * @param name tag name
+ * @param attList attribute list
+ * @param endSlash flag indicating whether the element is closed with
+ * an ending slash (xhtml-compliant)
+ */
+ void onStartElement(String name, AttributeList attList, boolean endSlash)
+ throws IOException;
+
+ /**
+ * Receive notification of the end of an element.
+ * @param name tag name
+ */
+ void onEndElement(String name)
+ throws IOException;
+
+ /**
+ * Receive notification of parsing start.
+ */
+ void onStart() throws IOException;
+
+ /**
+ * Receive notification of parsing end.
+ */
+ void onEnd() throws IOException;
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParser.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParser.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParser.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParser.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,468 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom;
+
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * HTML parser. Invokes a <code>DocumentHandler</code> whenever an event occurs.
+ */
+public final class HtmlParser {
+
+ /** Internal character buffer */
+ private final CharArrayWriter buffer = new CharArrayWriter(256);
+
+ /** Tag tokenizer */
+ private final TagTokenizer tokenizer = new TagTokenizer();
+
+ /** Tag name buffer */
+ private final CharArrayWriter tagNameBuffer = new CharArrayWriter(30);
+
+ /** Tag name */
+ private String tagName;
+
+ /** Registered document handler */
+ private final DocumentHandler documentHandler;
+
+ private enum PARSE_STATE {
+ OUTSIDE,
+ TAG,
+ SCRIPT,
+ COMMENT,
+ STRING,
+ EXPRESSION
+ }
+
+ /** Tag type constant */
+ private final static int TT_NONE = 0;
+
+ /** Tag type constant */
+ private final static int TT_MAYBE = 1;
+
+ /** Tag type constant */
+ private final static int TT_TAG = 2;
+
+ /** Expression state constant */
+ private final static int EXPR_NONE = 0;
+
+ /** Expression state constant */
+ private final static int EXPR_MAYBE = 1;
+
+ /** Parse state */
+ private PARSE_STATE parseState = PARSE_STATE.OUTSIDE;
+
+ /** Parse substate */
+ private int parseSubState;
+
+ /** Previous parse state */
+ private PARSE_STATE prevParseState;
+
+ /** Current tag type */
+ private int tagType;
+
+ /** Expression type */
+ private int exprType;
+
+ /** Quote character */
+ private char quoteChar;
+
+ public static void parse(final Reader reader, final DocumentHandler documentHandler)
+ throws IOException {
+ final HtmlParser parser = new HtmlParser(documentHandler);
+ parser.parse(reader);
+ }
+
+ /**
+ * Default constructor.
+ */
+ private HtmlParser(final DocumentHandler documentHandler) {
+ this.documentHandler = documentHandler;
+ }
+
+ private void parse(final Reader reader)
+ throws IOException {
+ try {
+ this.documentHandler.onStart();
+ final char[] readBuffer = new char[2048];
+ int readLen = 0;
+ while ( (readLen = reader.read(readBuffer)) > 0 ) {
+ this.update(readBuffer, readLen);
+ }
+ this.flushBuffer();
+ this.documentHandler.onEnd();
+ } finally {
+ try {
+ reader.close();
+ } catch ( final IOException ignore) {
+ // ignore
+ }
+ }
+ }
+
+ /**
+ * Feed characters to the parser.
+ *
+ * @param buf character buffer
+ * @param len length of affected buffer
+ */
+ private void update(final char[] buf, int len) throws IOException {
+ int start = 0;
+ final int end = len;
+
+ for (int curr = start; curr < end; curr++) {
+ final char c = buf[curr];
+
+ switch (parseState) {
+ case OUTSIDE:
+ if (c == '<') {
+ if (curr > start) {
+ documentHandler.onCharacters(buf, start, curr - start);
+ }
+ start = curr;
+ parseState = PARSE_STATE.TAG;
+ parseSubState = 0;
+ tagType = TT_MAYBE;
+ resetTagName();
+ } else if (c == '$') {
+ exprType = EXPR_MAYBE;
+ parseState = PARSE_STATE.EXPRESSION;
+ }
+ break;
+ case TAG:
+ switch (parseSubState) {
+ case -1:
+ if (c == '"' || c == '\'') {
+ quoteChar = c;
+ prevParseState = parseState;
+ parseState = PARSE_STATE.STRING;
+ parseSubState = -1;
+ } else if (c == '>') {
+ parseState = PARSE_STATE.OUTSIDE;
+ }
+ break;
+ case 0:
+ if (c == '!') {
+ parseState = PARSE_STATE.COMMENT;
+ parseSubState = 0;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ prevParseState = parseState;
+ parseState = PARSE_STATE.STRING;
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else if (c == '>') {
+ parseState = PARSE_STATE.OUTSIDE;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else if (!Character.isWhitespace(c)) {
+ tagNameBuffer.write(c);
+ parseSubState = 1;
+ } else {
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ }
+ break;
+ case 1:
+ if (c == '"' || c == '\'') {
+ tagType = TT_TAG;
+ parseSubState = 2;
+ quoteChar = c;
+ prevParseState = parseState;
+ parseState = PARSE_STATE.STRING;
+ } else if (c == '>') {
+ parseState = processTag(buf, start, curr - start + 1) ? PARSE_STATE.SCRIPT : PARSE_STATE.OUTSIDE;
+ start = curr + 1;
+ tagType = TT_NONE;
+ parseSubState = 0;
+ } else if (Character.isWhitespace(c)) {
+ tagType = TT_TAG;
+ parseSubState = 2;
+ } else {
+ tagNameBuffer.write(c);
+ }
+ break;
+ case 2:
+ if (c == '"' || c == '\'') {
+ quoteChar = c;
+ prevParseState = parseState;
+ parseState = PARSE_STATE.STRING;
+ } else if (c == '>') {
+ if (tagType == TT_TAG) {
+ parseState = processTag(buf, start, curr - start + 1) ? PARSE_STATE.SCRIPT : PARSE_STATE.OUTSIDE;
+ start = curr + 1;
+ } else {
+ flushBuffer();
+ parseState = "SCRIPT".equalsIgnoreCase(getTagName()) ? PARSE_STATE.SCRIPT : PARSE_STATE.OUTSIDE;
+ }
+ tagType = TT_NONE;
+ parseSubState = 0;
+ }
+ break;
+ }
+ break;
+ case COMMENT:
+ switch (parseSubState) {
+ case 0:
+ if (c == '-') {
+ parseSubState++;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ prevParseState = PARSE_STATE.TAG;
+ parseState = PARSE_STATE.STRING;
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else if (c == '>') {
+ parseState = PARSE_STATE.OUTSIDE;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else {
+ parseState = PARSE_STATE.TAG;
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ }
+ break;
+ case 1:
+ if (c == '-') {
+ parseSubState++;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ prevParseState = PARSE_STATE.TAG;
+ parseState = PARSE_STATE.STRING;
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else if (c == '>') {
+ parseState = PARSE_STATE.OUTSIDE;
+ tagType = TT_NONE;
+ flushBuffer();
+ } else {
+ parseState = PARSE_STATE.TAG;
+ parseSubState = -1;
+ tagType = TT_NONE;
+ flushBuffer();
+ }
+ break;
+ case 2:
+ if (c == '-') {
+ parseSubState++;
+ }
+ break;
+ case 3:
+ if (c == '-') {
+ parseSubState++;
+ } else {
+ parseSubState = 2;
+ }
+ break;
+ case 4:
+ if (c == '>') {
+ parseState = PARSE_STATE.OUTSIDE;
+ documentHandler.onComment(new String(buf, start, curr - start + 1));
+ start = curr + 1;
+ } else {
+ parseSubState = 2;
+ }
+ break;
+ }
+ break;
+
+ case SCRIPT:
+ switch (parseSubState) {
+ case 0:
+ if (c == '<') {
+ if (curr > start) {
+ documentHandler.onCharacters(buf, start, curr - start);
+ }
+ start = curr;
+ tagType = TT_MAYBE;
+ parseSubState++;
+ }
+ break;
+ case 1:
+ if (c == '/') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 2:
+ if (c == 'S' || c == 's') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 3:
+ if (c == 'C' || c == 'c') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 4:
+ if (c == 'R' || c == 'r') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 5:
+ if (c == 'I' || c == 'i') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 6:
+ if (c == 'P' || c == 'p') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 7:
+ if (c == 'T' || c == 't') {
+ parseSubState++;
+ } else {
+ tagType = TT_NONE;
+ parseSubState = 0;
+ flushBuffer();
+ }
+ break;
+ case 8:
+ if (c == '>') {
+ processTag(buf, start, curr - start + 1);
+ start = curr + 1;
+ tagType = TT_NONE;
+ parseState = PARSE_STATE.OUTSIDE;
+ }
+ break;
+ }
+ break;
+
+ case STRING:
+ if (c == quoteChar) {
+ parseState = prevParseState;
+ }
+ break;
+
+ case EXPRESSION:
+ if (exprType == EXPR_MAYBE && c != '{') {
+ // not a valid expression
+ if (c == '<') {
+ //reset to process tag correctly
+ curr--;
+ }
+ parseState = PARSE_STATE.OUTSIDE;
+ } else if (c == '}') {
+ parseState = PARSE_STATE.OUTSIDE;
+ }
+ exprType = EXPR_NONE;
+ break;
+ }
+ }
+ if (start < end) {
+ if (tagType == TT_NONE) {
+ documentHandler.onCharacters(buf, start, end - start);
+ } else {
+ buffer.write(buf, start, end - start);
+ }
+ }
+ }
+
+ /**
+ * Clears the internal tagname buffer and cache
+ */
+ private void resetTagName() {
+ tagName = null;
+ tagNameBuffer.reset();
+ }
+
+ /**
+ * Returns the tagname scanned and resets the internal tagname buffer
+ *
+ * @return tagname
+ */
+ private String getTagName() {
+ if (tagName == null) {
+ tagName = tagNameBuffer.toString();
+ }
+ return tagName;
+ }
+
+ /**
+ * Flush internal buffer. This forces the parser to flush the characters
+ * still held in its internal buffer, if the parsing state allows.
+ */
+ private void flushBuffer() throws IOException {
+ if (buffer.size() > 0) {
+ final char[] chars = buffer.toCharArray();
+ documentHandler.onCharacters(chars, 0, chars.length);
+ buffer.reset();
+ }
+ }
+
+ /**
+ * Decompose a tag and feed it to the document handler.
+ *
+ * @param ch
+ * character data
+ * @param off
+ * offset where character data starts
+ * @param len
+ * length of character data
+ */
+ private boolean processTag(char[] ch, int off, int len) throws IOException {
+ buffer.write(ch, off, len);
+
+ final char[] snippet = buffer.toCharArray();
+
+ tokenizer.tokenize(snippet, 0, snippet.length);
+ if (!tokenizer.endTag()) {
+ documentHandler.onStartElement(tokenizer.tagName(), tokenizer
+ .attributes(), tokenizer
+ .endSlash());
+ } else {
+ documentHandler.onEndElement(tokenizer.tagName());
+ }
+
+ buffer.reset();
+ return "SCRIPT".equalsIgnoreCase(tokenizer.tagName()) && !tokenizer.endSlash();
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParserService.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParserService.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParserService.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/HtmlParserService.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom;
+
+import org.apache.sling.scripting.sightly.html.dom.template.Template;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateParser;
+import org.apache.sling.scripting.sightly.compiler.api.MarkupHandler;
+import org.apache.sling.scripting.sightly.compiler.api.MarkupParser;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.scripting.sightly.compiler.api.MarkupHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.StringReader;
+
+@Component
+@Properties({
+ @Property(name="service.description", value="Sightly Simple HTML parser"),
+ @Property(name="service.ranking", intValue=1000)
+})
+@Service(MarkupParser.class)
+public class HtmlParserService implements MarkupParser {
+
+ private static final Logger log = LoggerFactory.getLogger(HtmlParserService.class);
+
+ public void parse(String script, MarkupHandler handler) {
+ try {
+ final StringReader sr = new StringReader(script);
+ final TemplateParser parser = new TemplateParser();
+ final Template template = parser.parse(sr);
+ // walk through the tree and send events
+ TreeTraverser tree = new TreeTraverser(handler);
+ tree.traverse(template);
+ } catch (IOException e) {
+ log.error("Failed to parse Sightly template", e);
+ }
+ }
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TagTokenizer.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TagTokenizer.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TagTokenizer.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TagTokenizer.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,514 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom;
+
+import java.io.CharArrayWriter;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tokenizes a snippet of characters into a structured tag/attribute name list.
+ */
+class TagTokenizer {
+ /** Tag name buffer */
+ private final CharArrayWriter tagName = new CharArrayWriter(30);
+
+ /** Attribute name buffer */
+ private final CharArrayWriter attName = new CharArrayWriter(30);
+
+ /** Attribute value buffer */
+ private final CharArrayWriter attValue = new CharArrayWriter(30);
+
+ /** Internal property list */
+ private final AttributeListImpl attributes = new AttributeListImpl();
+
+ /** Parse state constant */
+ private final static int START = 0;
+
+ /** Parse state constant */
+ private final static int TAG = START + 1;
+
+ /** Parse state constant */
+ private final static int NAME = TAG + 1;
+
+ /** Parse state constant */
+ private final static int INSIDE = NAME + 1;
+
+ /** Parse state constant */
+ private final static int ATTNAME = INSIDE + 1;
+
+ /** Parse state constant */
+ private final static int EQUAL = ATTNAME + 1;
+
+ /** Parse state constant */
+ private final static int ATTVALUE = EQUAL + 1;
+
+ /** Parse state constant */
+ private final static int STRING = ATTVALUE + 1;
+
+ /** Parse state constant */
+ private final static int ENDSLASH = STRING + 1;
+
+ /** Parse state constant */
+ private final static int END = ENDSLASH + 1;
+
+ /** Parse state constant */
+ private final static int BETWEEN_ATTNAME = END + 1;
+
+ /** Quote character */
+ private char quoteChar = '"';
+
+ /** Flag indicating whether the tag scanned is an end tag */
+ private boolean endTag;
+
+ /** Flag indicating whether an ending slash was parsed */
+ private boolean endSlash;
+
+ /** temporary flag indicating if attribute has a value */
+ private boolean hasAttributeValue;
+
+ /**
+ * Scan characters passed to this parser
+ */
+ public void tokenize(char[] buf, int off, int len) {
+ reset();
+
+ int parseState = START;
+
+ for (int i = 0; i < len; i++) {
+ char c = buf[off + i];
+
+ switch (parseState) {
+ case START:
+ if (c == '<') {
+ parseState = TAG;
+ }
+ break;
+ case TAG:
+ if (c == '/') {
+ endTag = true;
+ parseState = NAME;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ parseState = STRING;
+ } else if (Character.isWhitespace(c)) {
+ parseState = INSIDE;
+ } else {
+ tagName.write(c);
+ parseState = NAME;
+ }
+ break;
+ case NAME:
+ if (Character.isWhitespace(c)) {
+ parseState = INSIDE;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ parseState = STRING;
+ } else if (c == '>') {
+ parseState = END;
+ } else if (c == '/') {
+ parseState = ENDSLASH;
+ } else {
+ tagName.write(c);
+ }
+ break;
+ case INSIDE:
+ if (c == '>') {
+ attributeEnded();
+ parseState = END;
+ } else if (c == '/') {
+ attributeEnded();
+ parseState = ENDSLASH;
+ } else if (c == '"' || c == '\'') {
+ attributeValueStarted();
+ quoteChar = c;
+ parseState = STRING;
+ } else if (c == '=') {
+ parseState = EQUAL;
+ } else if (!Character.isWhitespace(c)) {
+ attName.write(c);
+ parseState = ATTNAME;
+ }
+ break;
+ case ATTNAME:
+ if (c == '>') {
+ attributeEnded();
+ parseState = END;
+ } else if (c == '/') {
+ attributeEnded();
+ parseState = ENDSLASH;
+ } else if (c == '=') {
+ parseState = EQUAL;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ parseState = STRING;
+ } else if (Character.isWhitespace(c)) {
+ parseState = BETWEEN_ATTNAME;
+ } else {
+ attName.write(c);
+ }
+ break;
+ case BETWEEN_ATTNAME:
+ if (c == '>') {
+ attributeEnded();
+ parseState = END;
+ } else if (c == '/') {
+ attributeEnded();
+ parseState = ENDSLASH;
+ } else if (c == '"' || c == '\'') {
+ attributeValueStarted();
+ quoteChar = c;
+ parseState = STRING;
+ } else if (c == '=') {
+ parseState = EQUAL;
+ } else if (!Character.isWhitespace(c)) {
+ attributeEnded();
+ attName.write(c);
+ parseState = ATTNAME;
+ }
+ break;
+ case EQUAL:
+ if (c == '>') {
+ attributeEnded();
+ parseState = END;
+ } else if (c == '"' || c == '\'') {
+ attributeValueStarted();
+ quoteChar = c;
+ parseState = STRING;
+ } else if (!Character.isWhitespace(c)) {
+ attributeValueStarted();
+ attValue.write(c);
+ parseState = ATTVALUE;
+ }
+ break;
+ case ATTVALUE:
+ if (Character.isWhitespace(c)) {
+ attributeEnded();
+ parseState = INSIDE;
+ } else if (c == '"' || c == '\'') {
+ attributeEnded();
+ quoteChar = c;
+ parseState = STRING;
+ } else if (c == '>') {
+ attributeEnded();
+ parseState = END;
+ } else {
+ attValue.write(c);
+ }
+ break;
+ case STRING:
+ if (c == quoteChar) {
+ attributeEnded();
+ parseState = INSIDE;
+ } else {
+ attValue.write(c);
+ }
+ break;
+ case ENDSLASH:
+ if (c == '>') {
+ endSlash = true;
+ parseState = END;
+ } else if (c == '"' || c == '\'') {
+ quoteChar = c;
+ parseState = STRING;
+ } else if (c != '/' && !Character.isWhitespace(c)) {
+ attName.write(c);
+ parseState = ATTNAME;
+ } else {
+ parseState = INSIDE;
+ }
+ break;
+ case END:
+ break;
+
+ }
+ }
+ }
+
+ /**
+ * Return a flag indicating whether the tag scanned was an end tag
+ * @return <code>true</code> if it was an end tag, otherwise
+ * <code>false</code>
+ */
+ public boolean endTag() {
+ return endTag;
+ }
+
+ /**
+ * Return a flag indicating whether an ending slash was scanned
+ * @return <code>true</code> if an ending slash was scanned, otherwise
+ * <code>false</code>
+ */
+ public boolean endSlash() {
+ return endSlash;
+ }
+
+ /**
+ * Return the tagname scanned
+ * @return tag name
+ */
+ public String tagName() {
+ return tagName.toString();
+ }
+
+ /**
+ * Return the list of attributes scanned
+ * @return list of attributes
+ */
+ public AttributeList attributes() {
+ return attributes;
+ }
+
+ /**
+ * Reset the internal state of the tokenizer
+ */
+ private void reset() {
+ tagName.reset();
+ attributes.reset();
+ endTag = false;
+ endSlash = false;
+ }
+
+ /**
+ * Invoked when an attribute ends
+ */
+ private void attributeEnded() {
+ if (attName.size() > 0) {
+ if (hasAttributeValue) {
+ attributes.addAttribute(attName.toString(), attValue.toString(),
+ quoteChar);
+ } else {
+ attributes.addAttribute(attName.toString(), quoteChar);
+
+ }
+ attName.reset();
+ attValue.reset();
+ hasAttributeValue = false;
+ }
+ }
+
+ /**
+ * Invoked when an attribute value starts
+ */
+ private void attributeValueStarted() {
+ hasAttributeValue = true;
+ }
+
+ /**
+ * Retransfers the tokenized tag data into html again
+ * @return the reassembled html string
+ */
+ public String toHtmlString() {
+ StringBuffer sb = new StringBuffer();
+ if (endTag) {
+ sb.append("</" + tagName());
+ } else {
+ sb.append("<" + tagName());
+ Iterator<String> attNames = attributes().attributeNames();
+ while (attNames.hasNext()) {
+ String attName = attNames.next();
+ String attValue = attributes().getQuotedValue(attName);
+
+ sb.append(" ");
+ sb.append(attName);
+ if (attValue != null) {
+ sb.append('=');
+ sb.append(attValue);
+ }
+ }
+ if (endSlash) {
+ sb.append(" /");
+ }
+ }
+ sb.append(">");
+ return sb.toString();
+ }
+}
+
+/**
+ * Internal implementation of an <code>AttributeList</code>
+ */
+class AttributeListImpl implements AttributeList {
+
+ /**
+ * Internal Value class
+ */
+ static class Value {
+
+ /**
+ * Create a new <code>Value</code> instance
+ */
+ public Value(char quoteChar, String value) {
+ this.quoteChar = quoteChar;
+ this.value = value;
+ }
+
+ /** Quote character */
+ public final char quoteChar;
+
+ /** Value itself */
+ public final String value;
+
+ /** String representation */
+ private String stringRep;
+
+ /**
+ * @see Object#toString()
+ */
+ @Override
+ public String toString() {
+ if (stringRep == null) {
+ stringRep = quoteChar + value + quoteChar;
+ }
+ return stringRep;
+ }
+ }
+
+ /** Attribute/Value pair map with case insensitives names */
+ private final Map<String, Value> attributes = new LinkedHashMap<String, Value>();
+
+ /** Attribute names, case sensitive */
+ private final Set<String> attributeNames = new LinkedHashSet<String>();
+
+ /** Flag indicating whether this object was modified */
+ private boolean modified;
+
+ /**
+ * Add an attribute/value pair to this attribute list
+ */
+ public void addAttribute(String name, String value, char quoteChar) {
+ attributes.put(name.toUpperCase(), new Value(quoteChar, value));
+ attributeNames.add(name);
+ }
+
+ /**
+ * Add an attribute/value pair to this attribute list
+ */
+ public void addAttribute(String name, char quoteChar) {
+ attributes.put(name.toUpperCase(), null);
+ attributeNames.add(name);
+ }
+
+ /**
+ * Empty this attribute list
+ */
+ public void reset() {
+ attributes.clear();
+ attributeNames.clear();
+ modified = false;
+ }
+
+ /**
+ * @see AttributeList#attributeCount
+ */
+ public int attributeCount() {
+ return attributes.size();
+ }
+
+ /**
+ * @see AttributeList#attributeNames
+ */
+ public Iterator<String> attributeNames() {
+ return attributeNames.iterator();
+ }
+
+ /**
+ * @see AttributeList#containsAttribute(String)
+ */
+ public boolean containsAttribute(String name) {
+ return attributes.containsKey(name.toUpperCase());
+ }
+
+ /**
+ * @see AttributeList#getValue(String)
+ */
+ public String getValue(String name) {
+ Value value = getValueEx(name);
+ if (value != null) {
+ return value.value;
+ }
+ return null;
+ }
+
+ /**
+ * @see AttributeList#getQuoteChar(java.lang.String)
+ */
+ public char getQuoteChar(String name) {
+ Value value = getValueEx(name);
+ if (value != null) {
+ return value.quoteChar;
+ }
+ return 0;
+ }
+
+ /**
+ * @see AttributeList#getQuotedValue(String)
+ */
+ public String getQuotedValue(String name) {
+ Value value = getValueEx(name);
+ if (value != null) {
+ return value.toString();
+ }
+ return null;
+ }
+
+ /**
+ * @see AttributeList#setValue(String, String)
+ */
+ public void setValue(String name, String value) {
+ if (value == null) {
+ removeValue(name);
+ } else {
+ Value old = getValueEx(name);
+ if (old == null) {
+ addAttribute(name, value, '"');
+ modified = true;
+ } else if (!old.value.equals(value)) {
+ addAttribute(name, value, old.quoteChar);
+ modified = true;
+ }
+ }
+ }
+
+ /**
+ * @see AttributeList#removeValue(String)
+ */
+ public void removeValue(String name) {
+ attributeNames.remove(name);
+ attributes.remove(name.toUpperCase());
+ modified = true;
+ }
+
+ /**
+ * @see AttributeList#isModified
+ */
+ public boolean isModified() {
+ return modified;
+ }
+
+ /**
+ * Return internal value structure
+ */
+ protected Value getValueEx(String name) {
+ return attributes.get(name.toUpperCase());
+ }
+}
\ No newline at end of file
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TreeTraverser.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TreeTraverser.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TreeTraverser.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/TreeTraverser.java Fri Nov 14 14:04:56 2014
@@ -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.sling.scripting.sightly.html.dom;
+
+import org.apache.sling.scripting.sightly.compiler.api.MarkupHandler;
+import org.apache.sling.scripting.sightly.html.dom.template.Template;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateElementNode;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateNode;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateTextNode;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateCommentNode;
+import org.apache.sling.scripting.sightly.html.dom.template.TemplateAttribute;
+import org.apache.sling.scripting.sightly.compiler.api.MarkupHandler;
+
+public class TreeTraverser {
+
+ private final MarkupHandler handler;
+
+ public TreeTraverser(MarkupHandler handler) {
+ this.handler = handler;
+ }
+
+ public void traverse(Template template) {
+ traverseNode(template);
+ this.handler.onDocumentFinished();
+ }
+
+ private void traverseNode(TemplateNode node) {
+ if (node instanceof TemplateElementNode) {
+ traverseElement((TemplateElementNode) node);
+ } else if (node instanceof TemplateTextNode) {
+ traverseText((TemplateTextNode) node);
+ } else if (node instanceof TemplateCommentNode) {
+ traverseComment((TemplateCommentNode) node);
+ } else {
+ throw new IllegalArgumentException("Unknown node type");
+ }
+ }
+
+ private void traverseElement(TemplateElementNode elem) {
+ if ("ROOT".equalsIgnoreCase(elem.getName())) {
+ traverseChildren(elem);
+ return;
+ }
+ String tagName = elem.getName();
+
+ if (elem.isHasStartElement()) {
+ handler.onOpenTagStart("<" + tagName, tagName);
+ for (TemplateAttribute attribute : elem.getAttributes()) {
+ handler.onAttribute(attribute.getName(), attribute.getValue());
+ }
+ if (elem.isHasEndSlash()) {
+ handler.onOpenTagEnd("/>");
+ } else {
+ handler.onOpenTagEnd(">");
+ }
+ } else {
+ handler.onOpenTagStart("", tagName);
+ handler.onOpenTagEnd("");
+ }
+
+ traverseChildren(elem);
+
+ if (elem.isHasEndElement()) {
+ handler.onCloseTag("</" + elem.getName() + ">");
+ } else {
+ handler.onCloseTag("");
+ }
+ }
+
+ private void traverseText(TemplateTextNode textNode) {
+ handler.onText(textNode.getText());
+ }
+
+ private void traverseComment(TemplateCommentNode comment) {
+ handler.onComment(comment.getText());
+ }
+
+ private void traverseChildren(TemplateElementNode elem) {
+ for (TemplateNode node : elem.getChildren()) {
+ traverseNode(node);
+ }
+ }
+
+}
Added: sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/template/Template.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/template/Template.java?rev=1639641&view=auto
==============================================================================
--- sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/template/Template.java (added)
+++ sling/trunk/contrib/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/html/dom/template/Template.java Fri Nov 14 14:04:56 2014
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ ******************************************************************************/
+package org.apache.sling.scripting.sightly.html.dom.template;
+
+
+public class Template extends TemplateElementNode {
+
+ public Template() {
+ super("ROOT", false, null);
+ }
+}