use of freemarker.template.TemplateSequenceModel in project freemarker by apache.
the class ElementModel method get.
/**
* An Element node supports various hash keys.
* Any key that corresponds to the tag name of any child elements
* returns a sequence of those elements. The special key "*" returns
* all the element's direct children.
* The "**" key return all the element's descendants in the order they
* occur in the document.
* Any key starting with '@' is taken to be the name of an element attribute.
* The special key "@@" returns a hash of all the element's attributes.
* The special key "/" returns the root document node associated with this element.
*/
@Override
public TemplateModel get(String key) throws TemplateModelException {
if (key.equals("*")) {
NodeListModel ns = new NodeListModel(this);
TemplateSequenceModel children = getChildNodes();
int size = children.size();
for (int i = 0; i < size; i++) {
NodeModel child = (NodeModel) children.get(i);
if (child.node.getNodeType() == Node.ELEMENT_NODE) {
ns.add(child);
}
}
return ns;
} else if (key.equals("**")) {
return new NodeListModel(((Element) node).getElementsByTagName("*"), this);
} else if (key.startsWith("@")) {
if (key.startsWith("@@")) {
if (key.equals(AtAtKey.ATTRIBUTES.getKey())) {
return new NodeListModel(node.getAttributes(), this);
} else if (key.equals(AtAtKey.START_TAG.getKey())) {
NodeOutputter nodeOutputter = new NodeOutputter(node);
return new SimpleScalar(nodeOutputter.getOpeningTag((Element) node));
} else if (key.equals(AtAtKey.END_TAG.getKey())) {
NodeOutputter nodeOutputter = new NodeOutputter(node);
return new SimpleScalar(nodeOutputter.getClosingTag((Element) node));
} else if (key.equals(AtAtKey.ATTRIBUTES_MARKUP.getKey())) {
StringBuilder buf = new StringBuilder();
NodeOutputter nu = new NodeOutputter(node);
nu.outputContent(node.getAttributes(), buf);
return new SimpleScalar(buf.toString().trim());
} else if (key.equals(AtAtKey.PREVIOUS_SIBLING_ELEMENT.getKey())) {
Node previousSibling = node.getPreviousSibling();
while (previousSibling != null && !this.isSignificantNode(previousSibling)) {
previousSibling = previousSibling.getPreviousSibling();
}
return previousSibling != null && previousSibling.getNodeType() == Node.ELEMENT_NODE ? wrap(previousSibling) : new NodeListModel(Collections.emptyList(), null);
} else if (key.equals(AtAtKey.NEXT_SIBLING_ELEMENT.getKey())) {
Node nextSibling = node.getNextSibling();
while (nextSibling != null && !this.isSignificantNode(nextSibling)) {
nextSibling = nextSibling.getNextSibling();
}
return nextSibling != null && nextSibling.getNodeType() == Node.ELEMENT_NODE ? wrap(nextSibling) : new NodeListModel(Collections.emptyList(), null);
} else {
// We don't know anything like this that's element-specific; fall back
return super.get(key);
}
} else {
// Starts with "@", but not with "@@"
if (DomStringUtil.isXMLNameLike(key, 1)) {
Attr att = getAttribute(key.substring(1));
if (att == null) {
return new NodeListModel(this);
}
return wrap(att);
} else if (key.equals("@*")) {
return new NodeListModel(node.getAttributes(), this);
} else {
// We don't know anything like this that's element-specific; fall back
return super.get(key);
}
}
} else if (DomStringUtil.isXMLNameLike(key)) {
// We interpret key as an element name
NodeListModel result = ((NodeListModel) getChildNodes()).filterByName(key);
return result.size() != 1 ? result : result.get(0);
} else {
// We don't anything like this that's element-specific; fall back
return super.get(key);
}
}
use of freemarker.template.TemplateSequenceModel in project freemarker by apache.
the class NodeListModel method get.
public TemplateModel get(String key) throws TemplateModelException {
int size = size();
if (size == 1) {
NodeModel nm = (NodeModel) get(0);
return nm.get(key);
}
if (key.startsWith("@@")) {
if (key.equals(AtAtKey.MARKUP.getKey()) || key.equals(AtAtKey.NESTED_MARKUP.getKey()) || key.equals(AtAtKey.TEXT.getKey())) {
StringBuilder result = new StringBuilder();
for (int i = 0; i < size; i++) {
NodeModel nm = (NodeModel) get(i);
TemplateScalarModel textModel = (TemplateScalarModel) nm.get(key);
result.append(textModel.getAsString());
}
return new SimpleScalar(result.toString());
} else if (key.length() != 2) /* to allow "@@" to fall through */
{
// As @@... would cause exception in the XPath engine, we throw a nicer exception now.
if (AtAtKey.containsKey(key)) {
throw new TemplateModelException("\"" + key + "\" is only applicable to a single XML node, but it was applied on " + (size != 0 ? size + " XML nodes (multiple matches)." : "an empty list of XML nodes (no matches)."));
} else {
throw new TemplateModelException("Unsupported @@ key: " + key);
}
}
}
if (DomStringUtil.isXMLNameLike(key) || ((key.startsWith("@") && (DomStringUtil.isXMLNameLike(key, 1) || key.equals("@@") || key.equals("@*")))) || key.equals("*") || key.equals("**")) {
NodeListModel result = new NodeListModel(contextNode);
for (int i = 0; i < size; i++) {
NodeModel nm = (NodeModel) get(i);
if (nm instanceof ElementModel) {
TemplateSequenceModel tsm = (TemplateSequenceModel) ((ElementModel) nm).get(key);
if (tsm != null) {
int tsmSize = tsm.size();
for (int j = 0; j < tsmSize; j++) {
result.add(tsm.get(j));
}
}
}
}
if (result.size() == 1) {
return result.get(0);
}
return result;
}
XPathSupport xps = getXPathSupport();
if (xps != null) {
Object context = (size == 0) ? null : rawNodeList();
return xps.executeQuery(context, key);
} else {
throw new TemplateModelException("Can't try to resolve the XML query key, because no XPath support is available. " + "This is either malformed or an XPath expression: " + key);
}
}
use of freemarker.template.TemplateSequenceModel in project PublicCMS-preview by sanluan.
the class TemplateModelUtils method converStringArray.
/**
* @param model
* @return string array value
* @throws TemplateModelException
*/
public static String[] converStringArray(TemplateModel model) throws TemplateModelException {
if (model instanceof TemplateSequenceModel) {
TemplateSequenceModel smodel = (TemplateSequenceModel) model;
String[] values = new String[smodel.size()];
for (int i = 0; i < smodel.size(); i++) {
values[i] = converString(smodel.get(i));
}
return values;
}
String str = converString(model);
if (CommonUtils.notEmpty(str)) {
if (0 <= str.indexOf(COMMA_DELIMITED)) {
return StringUtils.split(str, COMMA_DELIMITED);
} else {
return StringUtils.split(str, BLANK_SPACE);
}
}
return null;
}
use of freemarker.template.TemplateSequenceModel in project freemarker by apache.
the class DynamicKeyName method dealWithRangeKey.
private TemplateModel dealWithRangeKey(TemplateModel targetModel, RangeModel range, Environment env) throws UnexpectedTypeException, InvalidReferenceException, TemplateException {
final TemplateSequenceModel targetSeq;
final String targetStr;
if (targetModel instanceof TemplateSequenceModel) {
targetSeq = (TemplateSequenceModel) targetModel;
targetStr = null;
} else {
targetSeq = null;
try {
targetStr = target.evalAndCoerceToPlainText(env);
} catch (NonStringException e) {
throw new UnexpectedTypeException(target, target.eval(env), "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC, NUMERICAL_KEY_LHO_EXPECTED_TYPES, env);
}
}
final int size = range.size();
final boolean rightUnbounded = range.isRightUnbounded();
final boolean rightAdaptive = range.isRightAdaptive();
// produces an empty sequence, which thus doesn't contain any illegal indexes.
if (!rightUnbounded && size == 0) {
return emptyResult(targetSeq != null);
}
final int firstIdx = range.getBegining();
if (firstIdx < 0) {
throw new _MiscTemplateException(keyExpression, "Negative range start index (", Integer.valueOf(firstIdx), ") isn't allowed for a range used for slicing.");
}
final int targetSize = targetStr != null ? targetStr.length() : targetSeq.size();
final int step = range.getStep();
// Right-bounded ranges at this point aren't empty, so the right index surely can't reach targetSize.
if (rightAdaptive && step == 1 ? firstIdx > targetSize : firstIdx >= targetSize) {
throw new _MiscTemplateException(keyExpression, "Range start index ", Integer.valueOf(firstIdx), " is out of bounds, because the sliced ", (targetStr != null ? "string" : "sequence"), " has only ", Integer.valueOf(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"), ". ", "(Note that indices are 0-based).");
}
final int resultSize;
if (!rightUnbounded) {
final int lastIdx = firstIdx + (size - 1) * step;
if (lastIdx < 0) {
if (!rightAdaptive) {
throw new _MiscTemplateException(keyExpression, "Negative range end index (", Integer.valueOf(lastIdx), ") isn't allowed for a range used for slicing.");
} else {
resultSize = firstIdx + 1;
}
} else if (lastIdx >= targetSize) {
if (!rightAdaptive) {
throw new _MiscTemplateException(keyExpression, "Range end index ", Integer.valueOf(lastIdx), " is out of bounds, because the sliced ", (targetStr != null ? "string" : "sequence"), " has only ", Integer.valueOf(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"), ". (Note that indices are 0-based).");
} else {
resultSize = Math.abs(targetSize - firstIdx);
}
} else {
resultSize = size;
}
} else {
resultSize = targetSize - firstIdx;
}
if (resultSize == 0) {
return emptyResult(targetSeq != null);
}
if (targetSeq != null) {
ArrayList /*<TemplateModel>*/
list = new ArrayList(resultSize);
int srcIdx = firstIdx;
for (int i = 0; i < resultSize; i++) {
list.add(targetSeq.get(srcIdx));
srcIdx += step;
}
// List items are already wrapped, so the wrapper will be null:
return new SimpleSequence(list, null);
} else {
final int exclEndIdx;
if (step < 0 && resultSize > 1) {
if (!(range.isAffactedByStringSlicingBug() && resultSize == 2)) {
throw new _MiscTemplateException(keyExpression, "Decreasing ranges aren't allowed for slicing strings (as it would give reversed text). " + "The index range was: first = ", Integer.valueOf(firstIdx), ", last = ", Integer.valueOf(firstIdx + (resultSize - 1) * step));
} else {
// Emulate the legacy bug, where "foo"[n .. n-1] gives "" instead of an error (if n >= 1).
// Fix this in FTL [2.4]
exclEndIdx = firstIdx;
}
} else {
exclEndIdx = firstIdx + resultSize;
}
return new SimpleScalar(targetStr.substring(firstIdx, exclEndIdx));
}
}
use of freemarker.template.TemplateSequenceModel in project freemarker by apache.
the class DynamicKeyName method dealWithNumericalKey.
private TemplateModel dealWithNumericalKey(TemplateModel targetModel, int index, Environment env) throws TemplateException {
if (targetModel instanceof TemplateSequenceModel) {
TemplateSequenceModel tsm = (TemplateSequenceModel) targetModel;
int size;
try {
size = tsm.size();
} catch (Exception e) {
size = Integer.MAX_VALUE;
}
return index < size ? tsm.get(index) : null;
}
try {
String s = target.evalAndCoerceToPlainText(env);
try {
return new SimpleScalar(s.substring(index, index + 1));
} catch (IndexOutOfBoundsException e) {
if (index < 0) {
throw new _MiscTemplateException("Negative index not allowed: ", Integer.valueOf(index));
}
if (index >= s.length()) {
throw new _MiscTemplateException("String index out of range: The index was ", Integer.valueOf(index), " (0-based), but the length of the string is only ", Integer.valueOf(s.length()), ".");
}
throw new RuntimeException("Can't explain exception", e);
}
} catch (NonStringException e) {
throw new UnexpectedTypeException(target, targetModel, "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC, NUMERICAL_KEY_LHO_EXPECTED_TYPES, (targetModel instanceof TemplateHashModel ? "You had a numberical value inside the []. Currently that's only supported for " + "sequences (lists) and strings. To get a Map item with a non-string key, " + "use myMap?api.get(myKey)." : null), env);
}
}
Aggregations