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.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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<Object> 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<Object> toList(Object obj) {
|
||||
if (obj instanceof Collection) {
|
||||
return new ArrayList<Object>((Collection) obj);
|
||||
} else if (obj instanceof Iterator) {
|
||||
Iterator<Object> it = (Iterator<Object>) obj;
|
||||
List<Object> lst = new ArrayList<Object>();
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user