Skip to content

Commit

Permalink
better and clean context lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
jknack committed Nov 12, 2015
1 parent c935fb2 commit ddf047f
Show file tree
Hide file tree
Showing 23 changed files with 806 additions and 368 deletions.
318 changes: 103 additions & 215 deletions handlebars/src/main/java/com/github/jknack/handlebars/Context.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Copyright (c) 2012-2015 Edgar Espina
*
* This file is part of Handlebars.java.
*
* Licensed 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 com.github.jknack.handlebars;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.github.jknack.handlebars.internal.path.DataPath;
import com.github.jknack.handlebars.internal.path.IndexedPath;
import com.github.jknack.handlebars.internal.path.ParentPath;
import com.github.jknack.handlebars.internal.path.PropertyPath;
import com.github.jknack.handlebars.internal.path.ResolveParentPath;
import com.github.jknack.handlebars.internal.path.ResolveThisPath;
import com.github.jknack.handlebars.internal.path.ThisPath;

/**
* Compile mustache/handlebars expressions.
*
* @author edgar
* @since 4.0.1.
*/
public final class PathCompiler {

/** Cache with path expressions. */
private static Map<String, List<PathExpression>> cache = new ConcurrentHashMap<>();

/** Split pattern. */
private static Pattern pattern = Pattern
.compile("((\\[[^\\[\\]]+])|([^" + Pattern.quote("./") + "]+))");

/**
* Not allowed.
*/
private PathCompiler() {
}

/**
* Split the property name by separator (except within a [] escaped blocked)
* and create an array of it.
*
* @param key The property's name.
* @return A path representation of the property (array based).
*/
public static List<PathExpression> compile(final String key) {
List<PathExpression> path = cache.get(key);
if (path == null) {
path = parse(key);
cache.put(key, path);
}
return path;
}

/**
* Split the property name by separator (except within a [] escaped blocked)
* and create an array of it.
*
* @param path The property's path.
* @return A path representation of the property (array based).
*/
private static List<PathExpression> parse(final String path) {
LinkedList<PathExpression> resolvers = new LinkedList<>();
if ("this".equals(path) || "./".equals(path) || ".".equals(path)) {
resolvers.add(new ResolveThisPath(path));
return resolvers;
}
if ("..".equals(path)) {
resolvers.add(new ResolveParentPath());
return resolvers;
}
if (path.startsWith("../")) {
resolvers.add(new ParentPath());
resolvers.addAll(parse(path.substring("../".length())));
return resolvers;
}
if (path.startsWith("./")) {
resolvers.add(new ThisPath("./"));
resolvers.addAll(parse(path.substring("./".length())));
return resolvers;
}
Matcher matcher = pattern.matcher(path);
while (matcher.find()) {
String key = matcher.group(1);
if ("this".equals(key)) {
resolvers.add(new ThisPath(key));
} else if (key.charAt(0) == '@') {
resolvers.add(new DataPath(key));
} else {
if (key.charAt(0) == '[' && key.charAt(key.length() - 1) == ']') {
key = key.substring(1, key.length() - 1);
}
try {
resolvers.add(new IndexedPath(Integer.parseInt(key), key));
} catch (NumberFormatException ex) {
resolvers.add(new PropertyPath(key));
}
}
}
return resolvers;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/**
* Copyright (c) 2012-2015 Edgar Espina
*
* This file is part of Handlebars.java.
*
* Licensed 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 com.github.jknack.handlebars;

/**
* Compiled version of path expression, like: <code>this</code>, <code>foo</code>,
* <code>foo.bar</code>.
*
* @author edgar
* @since 4.0.1
* @see PathCompiler#compile(String)
*/
public interface PathExpression {

/**
* Call the next expression in the chain and/or finalize the process if this was the tail.
*
* @author edgar
* @since 4.0.1
*/
interface Chain {

/**
* Call the next resolver in the chain or finish the call.
*
* @param resolver Value resolver.
* @param context Context object.
* @param data Data object.
* @return A resolved value or <code>null</code>.
*/
Object next(ValueResolver resolver, Context context, Object data);
}

/**
* Eval the expression and resolve it to a value.
*
* @param resolver Value resolver
* @param context Context object.
* @param data Data object.
* @param chain Expression chain.
* @return A resolved value or <code>null</code>.
*/
Object eval(ValueResolver resolver, Context context, Object data, Chain chain);

/**
* @return True if this expression is local. That's lookup won't be propagate to parent (or any
* other). Example of these expressions are: <code>this.name</code> <code>this</code>,
* etc...
*/
boolean local();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@
import java.util.Iterator;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;

import com.github.jknack.handlebars.Context;
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Options;
Expand Down Expand Up @@ -53,13 +51,12 @@ public class EachHelper implements Helper<Object> {
@Override
public CharSequence apply(final Object context, final Options options)
throws IOException {
if (context == null) {
return StringUtils.EMPTY;
}
if (context instanceof Iterable) {
return iterableContext((Iterable) context, options);
} else if (context != null) {
return hashContext(context, options);
}
return hashContext(context, options);
return options.buffer();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import com.github.jknack.handlebars.Helper;
import com.github.jknack.handlebars.Lambda;
import com.github.jknack.handlebars.Options;
import com.github.jknack.handlebars.PathCompiler;
import com.github.jknack.handlebars.PathExpression;
import com.github.jknack.handlebars.TagType;
import com.github.jknack.handlebars.Template;
import com.github.jknack.handlebars.helper.EachHelper;
Expand Down Expand Up @@ -105,6 +107,9 @@ class Block extends HelperResolver {
/** Tag type, default: is {@link TagType#SECTION}. */
protected TagType tagType;

/** Compiled path for {@link #name()}. */
private List<PathExpression> path;

/**
* Creates a new {@link Block}.
*
Expand All @@ -121,6 +126,7 @@ public Block(final Handlebars handlebars, final String name,
final Map<String, Object> hash, final List<String> blockParams) {
super(handlebars);
this.name = notNull(name, "The name is required.");
this.path = PathCompiler.compile(name);
this.inverted = inverted;
this.type = type;
params(params);
Expand Down Expand Up @@ -167,7 +173,7 @@ protected void merge(final Context context, final Writer writer) throws IOExcept
final Object it;
Context itCtx = context;
if (helper == null) {
it = Transformer.transform(context.get(name));
it = Transformer.transform(itCtx.get(this.path));
if (inverted) {
helperName = UnlessHelper.NAME;
} else if (it instanceof Iterable) {
Expand Down Expand Up @@ -198,14 +204,13 @@ protected void merge(final Context context, final Writer writer) throws IOExcept
it = Transformer.transform(determineContext(context));
}

Options options = new Options.Builder(handlebars, helperName, TagType.SECTION, itCtx,
template)
.setInverse(inverse)
.setParams(params(itCtx))
.setHash(hash(itCtx))
.setBlockParams(blockParams)
.setWriter(writer)
.build();
Options options = new Options.Builder(handlebars, helperName, tagType, itCtx, template)
.setInverse(inverse)
.setParams(params(itCtx))
.setHash(hash(itCtx))
.setBlockParams(blockParams)
.setWriter(writer)
.build();
options.data(Context.PARAM_SIZE, this.params.size());

CharSequence result = helper.apply(it, options);
Expand Down

0 comments on commit ddf047f

Please sign in to comment.