blob: 76c0f3a274ca196a3abddf0b39f7a54e33e19e0f [file] [log] [blame]
/*******************************************************************************
* 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.java.compiler;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import javax.script.Bindings;
import javax.script.SimpleBindings;
import org.apache.sling.scripting.sightly.Record;
import org.apache.sling.scripting.sightly.render.RenderContext;
/**
* Basic unit of rendering. This also extends the record interface. The properties for a unit are the sub-units.
*/
public abstract class RenderUnit implements Record<RenderUnit> {
private final Map<String, RenderUnit> subTemplates = new HashMap<>();
private Map<String, RenderUnit> siblings;
/**
* Render the main script template
*
* @param out the {@link PrintWriter} to which the commands are written
* @param renderContext the rendering context
* @param arguments the arguments for this unit
*/
public final void render(PrintWriter out, RenderContext renderContext, Bindings arguments) {
Bindings globalBindings = renderContext.getBindings();
render(out, buildGlobalScope(globalBindings), new CaseInsensitiveBindings(arguments), renderContext);
}
@Override
public RenderUnit getProperty(String name) {
return subTemplates.get(name.toLowerCase());
}
@Override
public Set<String> getPropertyNames() {
return subTemplates.keySet();
}
protected abstract void render(PrintWriter out,
Bindings bindings,
Bindings arguments,
RenderContext renderContext);
@SuppressWarnings({"unused", "unchecked"})
protected void callUnit(PrintWriter out, RenderContext renderContext, Object templateObj, Object argsObj) {
if (!(templateObj instanceof RenderUnit)) {
if (templateObj == null) {
throw new SightlyJavaCompilerException("data-sly-call: expression evaluates to null.");
}
if (renderContext.getObjectModel().isPrimitive(templateObj)) {
throw new SightlyJavaCompilerException(
"data-sly-call: primitive \"" + templateObj.toString() + "\" does not represent a HTL template.");
} else if (templateObj instanceof String) {
throw new SightlyJavaCompilerException(
"data-sly-call: String '" + templateObj.toString() + "' does not represent a HTL template.");
}
throw new SightlyJavaCompilerException(
"data-sly-call: " + templateObj.getClass().getName() + " does not represent a HTL template.");
}
RenderUnit unit = (RenderUnit) templateObj;
Map<String, Object> argumentsMap = renderContext.getObjectModel().toMap(argsObj);
Bindings arguments = new SimpleBindings(Collections.unmodifiableMap(argumentsMap));
unit.render(out, renderContext, arguments);
}
@SuppressWarnings("UnusedDeclaration")
protected FluentMap obj() {
return new FluentMap();
}
@SuppressWarnings("unused")
protected final void addSubTemplate(String name, RenderUnit renderUnit) {
renderUnit.setSiblings(subTemplates);
subTemplates.put(name.toLowerCase(), renderUnit);
}
private void setSiblings(Map<String, RenderUnit> siblings) {
this.siblings = siblings;
}
private Bindings buildGlobalScope(Bindings bindings) {
SimpleBindings simpleBindings = new SimpleBindings(bindings);
simpleBindings.putAll(bindings);
if (siblings != null) {
simpleBindings.putAll(siblings);
}
simpleBindings.putAll(subTemplates);
return new CaseInsensitiveBindings(simpleBindings);
}
protected static class FluentMap extends HashMap<String, Object> {
/**
* Fluent variant of put.
*
* @param name the name of the property
* @param value the value of the property
* @return this instance
*/
public FluentMap with(String name, Object value) {
put(name, value);
return this;
}
}
private static final class CaseInsensitiveBindings extends SimpleBindings {
private CaseInsensitiveBindings(Map<String, Object> m) {
for (Map.Entry<String, Object> entry : m.entrySet()) {
put(entry.getKey().toLowerCase(), entry.getValue());
}
}
@Override
public Object get(Object key) {
if (!(key instanceof String)) {
throw new ClassCastException("key should be a String");
}
return super.get(((String) key).toLowerCase());
}
@Override
public boolean containsKey(Object key) {
if (!(key instanceof String)) {
throw new ClassCastException("key should be a String");
}
return super.containsKey(((String) key).toLowerCase());
}
}
}