From 6cfc376e8d61f360c7cd7e11b3bead8da4e1695b Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Mon, 7 Oct 2013 14:25:58 -0700 Subject: [PATCH] Rewrote array/list fetch-by-index using collectors. That's what collectors are for. This also avoids the needless expense of doing Integer.parseInt on every single key (the vast majority of which will not actually be numbers), every single time we do a context lookup. --- .../samskivert/mustache/BasicCollector.java | 47 +++++++++++++++++-- .../com/samskivert/mustache/Template.java | 41 +--------------- 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/samskivert/mustache/BasicCollector.java b/src/main/java/com/samskivert/mustache/BasicCollector.java index f9278c1..bd324c2 100644 --- a/src/main/java/com/samskivert/mustache/BasicCollector.java +++ b/src/main/java/com/samskivert/mustache/BasicCollector.java @@ -6,6 +6,7 @@ package com.samskivert.mustache; import java.util.Collections; import java.util.Iterator; +import java.util.List; import java.util.Map; /** @@ -25,12 +26,16 @@ public abstract class BasicCollector implements Mustache.Collector public Mustache.VariableFetcher createFetcher (Object ctx, String name) { // support both .name and this.name to fetch members - if (name == Template.DOT_NAME || name == Template.THIS_NAME) { - return THIS_FETCHER; - } + if (name == Template.DOT_NAME || name == Template.THIS_NAME) return THIS_FETCHER; - if (ctx instanceof Map) { - return MAP_FETCHER; + if (ctx instanceof Map) return MAP_FETCHER; + + // if the name looks like a number, potentially use one of our 'indexing' fetchers + char c = name.charAt(0); + if (c >= '0' && c <= '9') { + if (ctx instanceof List) return LIST_FETCHER; + if (ctx instanceof Object[]) return ARRAY_FETCHER; + if (ctx instanceof Iterator) return ITER_FETCHER; } return null; @@ -47,6 +52,38 @@ public abstract class BasicCollector implements Mustache.Collector } }; + protected static final Mustache.VariableFetcher LIST_FETCHER = new Mustache.VariableFetcher() { + public Object get (Object ctx, String name) throws Exception { + try { + return ((List)ctx).get(Integer.parseInt(name)); + } catch (NumberFormatException nfe) { + return Template.NO_FETCHER_FOUND; + } + } + }; + + protected static final Mustache.VariableFetcher ARRAY_FETCHER = new Mustache.VariableFetcher() { + public Object get (Object ctx, String name) throws Exception { + try { + return ((Object[])ctx)[Integer.parseInt(name)]; + } catch (NumberFormatException nfe) { + return Template.NO_FETCHER_FOUND; + } + } + }; + + protected static final Mustache.VariableFetcher ITER_FETCHER = new Mustache.VariableFetcher() { + public Object get (Object ctx, String name) throws Exception { + try { + Iterator iter = (Iterator)ctx; + for (int ii = 0, ll = Integer.parseInt(name); ii < ll; ii++) iter.next(); + return iter.next(); + } catch (NumberFormatException nfe) { + return Template.NO_FETCHER_FOUND; + } + } + }; + protected static final Mustache.VariableFetcher THIS_FETCHER = new Mustache.VariableFetcher() { public Object get (Object ctx, String name) throws Exception { return ctx; diff --git a/src/main/java/com/samskivert/mustache/Template.java b/src/main/java/com/samskivert/mustache/Template.java index 2261ecd..c45aeb2 100644 --- a/src/main/java/com/samskivert/mustache/Template.java +++ b/src/main/java/com/samskivert/mustache/Template.java @@ -7,12 +7,8 @@ package com.samskivert.mustache; import java.io.IOException; import java.io.StringWriter; import java.io.Writer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.List; import java.util.Map; /** @@ -143,18 +139,7 @@ public class Template } // once we step into a composite key, we drop the ability to query our parent // contexts; that would be weird and confusing - String next = comps[ii].intern(); - Integer idx = toNumber(next); - if (idx != null) { - Object nextData = null; - List c = toList(data); - if (c != null && idx >= 0 && idx < c.size()) { - nextData = c.get(idx); - } - data = nextData; - } else { - data = getValueIn(data, next, line); - } + data = getValueIn(data, comps[ii].intern(), line); } return checkForMissing(name, line, missingIsNull, data); } @@ -184,30 +169,6 @@ public class Template return checkForMissing(name, line, missingIsNull, NO_FETCHER_FOUND); } - protected static List toList(Object obj) { - if (obj instanceof Collection) { - return new ArrayList((Collection) obj); - } else if (obj instanceof Iterator) { - Iterator it = (Iterator) obj; - List lst = new ArrayList(); - while (it.hasNext()) { - lst.add(it.next()); - } - return lst; - } else if (obj instanceof Object[]) { - return Arrays.asList((Object[]) obj); - } - return null; - } - - protected static Integer toNumber(String value) { - try { - return Integer.parseInt(value); - } catch (NumberFormatException ex) { - return null; - } - } - /** * Returns the value of the specified variable, noting that it is intended to be used as the * contents for a segment. Presently this does not do anything special, but eventually this