com.github.mustachejava.reflect.GuardedBinding Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of compiler Show documentation
Show all versions of compiler Show documentation
Implementation of mustache.js for Java
package com.github.mustachejava.reflect;
import com.github.mustachejava.*;
import com.github.mustachejava.codes.PartialCode;
import com.github.mustachejava.util.GuardException;
import com.github.mustachejava.util.Wrapper;
import java.util.Arrays;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Logger;
/**
* Codes are bound to their variables through bindings.
*
* User: sam
* Date: 7/7/12
* Time: 6:05 PM
*/
public class GuardedBinding implements Binding {
// Debug callsites
private static Logger logger = Logger.getLogger("mustache");
private static boolean debug = Boolean.getBoolean("mustache.debug");
private final ObjectHandler oh;
private final TemplateContext tc;
private final String name;
private final Code code;
public GuardedBinding(ObjectHandler oh, String name, TemplateContext tc, Code code) {
this.name = name;
this.code = code;
this.oh = oh;
this.tc = tc;
}
/**
* The chances of a new guard every time is very low. Instead we will
* store previously used guards and try them all before creating a new one.
*/
private Set previousSet = new CopyOnWriteArraySet();
private volatile Wrapper[] prevWrappers;
/**
* Retrieve the first value in the stacks of scopes that matches
* the give name. The method wrappers are cached and guarded against
* the type or number of scopes changing.
*
* Methods will be found using the object handler, called here with
* another lookup on a guard failure and finally coerced to a final
* value based on the ObjectHandler you provide.
*
* @param scopes An array of scopes to interrogate from right to left.
* @return The value of the field or method
*/
@Override
public Object get(Object[] scopes) {
// Loop over the wrappers and find the one that matches
// this set of scopes or get a new one
Wrapper current = null;
Wrapper[] wrappers = prevWrappers;
if (wrappers != null) {
for (Wrapper prevWrapper : wrappers) {
try {
current = prevWrapper;
return oh.coerce(prevWrapper.call(scopes));
} catch (GuardException ge) {
// Check the next one or create a new one
} catch (MustacheException me) {
throw new MustacheException("Failed: " + current, me);
}
}
}
return createAndGet(scopes);
}
private Object createAndGet(Object[] scopes) {
// Make a new wrapper for this set of scopes and add it to the set
Wrapper wrapper = getWrapper(name, scopes);
previousSet.add(wrapper);
if (prevWrappers == null || prevWrappers.length != previousSet.size()) {
prevWrappers = previousSet.toArray(new Wrapper[previousSet.size()]);
}
// If this fails the guard, there is a bug
try {
return oh.coerce(wrapper.call(scopes));
} catch (GuardException e) {
throw new AssertionError(
"Unexpected guard failure: " + previousSet + " " + Arrays.asList(scopes));
}
}
protected synchronized Wrapper getWrapper(String name, Object[] scopes) {
Wrapper wrapper = oh.find(name, scopes);
if (wrapper instanceof MissingWrapper) {
if (debug) {
// Ugly but generally not interesting
if (!(code instanceof PartialCode)) {
StringBuilder sb = new StringBuilder("Failed to find: ")
.append(name)
.append(" (")
.append(tc.file())
.append(":")
.append(tc.line())
.append(") ")
.append("in");
for (Object scope : scopes) {
if (scope != null) {
Class aClass = scope.getClass();
try {
sb.append(" ").append(aClass.getSimpleName());
} catch (Exception e) {
// Some generated classes don't have simple names
try {
sb.append(" ").append(aClass.getName());
} catch (Exception e1) {
// Some generated classes have proper names at all
}
}
}
}
logger.warning(sb.toString());
}
}
}
return wrapper;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy