0001: /*
0002: * Copyright (c) 2003 The Visigoth Software Society. All rights
0003: * reserved.
0004: *
0005: * Redistribution and use in source and binary forms, with or without
0006: * modification, are permitted provided that the following conditions
0007: * are met:
0008: *
0009: * 1. Redistributions of source code must retain the above copyright
0010: * notice, this list of conditions and the following disclaimer.
0011: *
0012: * 2. Redistributions in binary form must reproduce the above copyright
0013: * notice, this list of conditions and the following disclaimer in
0014: * the documentation and/or other materials provided with the
0015: * distribution.
0016: *
0017: * 3. The end-user documentation included with the redistribution, if
0018: * any, must include the following acknowledgement:
0019: * "This product includes software developed by the
0020: * Visigoth Software Society (http://www.visigoths.org/)."
0021: * Alternately, this acknowledgement may appear in the software itself,
0022: * if and wherever such third-party acknowledgements normally appear.
0023: *
0024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
0025: * project contributors may be used to endorse or promote products derived
0026: * from this software without prior written permission. For written
0027: * permission, please contact visigoths@visigoths.org.
0028: *
0029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
0030: * nor may "FreeMarker" or "Visigoth" appear in their names
0031: * without prior written permission of the Visigoth Software Society.
0032: *
0033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
0037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
0038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
0040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
0041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
0042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
0043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0044: * SUCH DAMAGE.
0045: * ====================================================================
0046: *
0047: * This software consists of voluntary contributions made by many
0048: * individuals on behalf of the Visigoth Software Society. For more
0049: * information on the Visigoth Software Society, please see
0050: * http://www.visigoths.org/
0051: */
0052:
0053: package freemarker.core;
0054:
0055: import freemarker.template.*;
0056:
0057: import java.io.UnsupportedEncodingException;
0058: import java.text.DateFormat;
0059: import java.text.NumberFormat;
0060: import java.util.Date;
0061: import java.util.HashMap;
0062: import java.util.Iterator;
0063: import java.util.List;
0064:
0065: import freemarker.template.utility.ClassUtil;
0066: import freemarker.template.utility.StringUtil;
0067: import freemarker.core.NodeBuiltins.*;
0068: import freemarker.core.NumericalBuiltins.*;
0069: import freemarker.core.SequenceBuiltins.*;
0070: import freemarker.core.StringBuiltins.*;
0071:
0072: /**
0073: * The ? operator used to get the
0074: * functionality of built-in unary operators
0075: * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
0076: */
0077: abstract class BuiltIn extends Expression implements Cloneable {
0078: Expression target;
0079: String key;
0080:
0081: static final HashMap builtins = new HashMap();
0082:
0083: static {
0084: // These are the only ones we have now.
0085: // We throw a parse exception if it's not one of these.
0086: builtins.put("ancestors", new ancestorsBI());
0087: builtins.put("byte", new byteBI());
0088: builtins.put("c", new cBI());
0089: builtins.put("cap_first", new cap_firstBI());
0090: builtins.put("capitalize", new capitalizeBI());
0091: builtins.put("children", new childrenBI());
0092: builtins.put("chop_linebreak", new chop_linebreakBI());
0093: builtins.put("contains", new containsBI());
0094: builtins.put("date", new dateBI(TemplateDateModel.DATE));
0095: builtins
0096: .put("datetime", new dateBI(TemplateDateModel.DATETIME));
0097: builtins.put("default", new defaultBI());
0098: builtins.put("double", new doubleBI());
0099: builtins.put("ends_with", new ends_withBI());
0100: builtins.put("eval", new evalBI());
0101: builtins.put("exists", new existsBI());
0102: builtins.put("first", new firstBI());
0103: builtins.put("float", new floatBI());
0104: builtins.put("chunk", new chunkBI());
0105: builtins.put("has_content", new has_contentBI());
0106: builtins.put("html", new htmlBI());
0107: builtins.put("if_exists", new if_existsBI());
0108: builtins.put("index_of", new index_ofBI());
0109: builtins.put("int", new intBI());
0110: builtins.put("interpret", new Interpret());
0111: builtins.put("is_boolean", new is_booleanBI());
0112: builtins.put("is_collection", new is_collectionBI());
0113: builtins.put("is_date", new is_dateBI());
0114: builtins.put("is_directive", new is_directiveBI());
0115: builtins.put("is_enumerable", new is_enumerableBI());
0116: builtins.put("is_hash_ex", new is_hash_exBI());
0117: builtins.put("is_hash", new is_hashBI());
0118: builtins.put("is_indexable", new is_indexableBI());
0119: builtins.put("is_macro", new is_macroBI());
0120: builtins.put("is_method", new is_methodBI());
0121: builtins.put("is_node", new is_nodeBI());
0122: builtins.put("is_number", new is_numberBI());
0123: builtins.put("is_sequence", new is_sequenceBI());
0124: builtins.put("is_string", new is_stringBI());
0125: builtins.put("is_transform", new is_transformBI());
0126: builtins.put("j_string", new j_stringBI());
0127: builtins.put("js_string", new js_stringBI());
0128: builtins.put("keys", new keysBI());
0129: builtins.put("last_index_of", new last_index_ofBI());
0130: builtins.put("last", new lastBI());
0131: builtins.put("left_pad", new left_padBI());
0132: builtins.put("length", new lengthBI());
0133: builtins.put("long", new longBI());
0134: builtins.put("lower_case", new lower_caseBI());
0135: builtins.put("namespace", new namespaceBI());
0136: builtins.put("new", new NewBI());
0137: builtins.put("node_name", new node_nameBI());
0138: builtins.put("node_namespace", new node_namespaceBI());
0139: builtins.put("node_type", new node_typeBI());
0140: builtins.put("number", new numberBI());
0141: builtins.put("parent", new parentBI());
0142: builtins.put("replace", new replaceBI());
0143: builtins.put("reverse", new reverseBI());
0144: builtins.put("right_pad", new right_padBI());
0145: builtins.put("root", new rootBI());
0146: builtins.put("rtf", new rtfBI());
0147: builtins.put("seq_contains", new seq_containsBI());
0148: builtins.put("seq_index_of", new seq_index_ofBI(1));
0149: builtins.put("seq_last_index_of", new seq_index_ofBI(-1));
0150: builtins.put("short", new shortBI());
0151: builtins.put("size", new sizeBI());
0152: builtins.put("sort_by", new sort_byBI());
0153: builtins.put("sort", new sortBI());
0154: builtins.put("split", new splitBI());
0155: builtins.put("starts_with", new starts_withBI());
0156: builtins.put("string", new stringBI());
0157: builtins.put("substring", new substringBI());
0158: builtins.put("time", new dateBI(TemplateDateModel.TIME));
0159: builtins.put("trim", new trimBI());
0160: builtins.put("uncap_first", new uncap_firstBI());
0161: builtins.put("upper_case", new upper_caseBI());
0162: builtins.put("url", new urlBI());
0163: builtins.put("values", new valuesBI());
0164: builtins.put("web_safe", builtins.get("html")); // deprecated; use ?html instead
0165: builtins.put("word_list", new word_listBI());
0166: builtins.put("xml", new xmlBI());
0167: try {
0168: Class.forName("java.util.regex.Pattern");
0169: builtins
0170: .put(
0171: "matches",
0172: instantiate("freemarker.core.RegexBuiltins$matchesBI"));
0173: builtins
0174: .put(
0175: "groups",
0176: instantiate("freemarker.core.RegexBuiltins$groupsBI"));
0177: builtins
0178: .put(
0179: "replace",
0180: instantiate("freemarker.core.RegexBuiltins$replace_reBI"));
0181: builtins
0182: .put(
0183: "split",
0184: instantiate("freemarker.core.RegexBuiltins$split_reBI"));
0185: } catch (Exception e) {
0186: }
0187: }
0188:
0189: private static Object instantiate(String className)
0190: throws Exception {
0191: return ClassUtil.forName(className).newInstance();
0192: }
0193:
0194: static BuiltIn newBuiltIn(Expression target, String key, Token tok,
0195: String templateName) throws ParseException {
0196: BuiltIn bi = (BuiltIn) builtins.get(key);
0197: if (bi == null) {
0198: String locationInfo = "Error on line " + tok.beginLine
0199: + ", column " + tok.beginColumn + ", in template "
0200: + templateName + "\n";
0201: StringBuffer buf = new StringBuffer("Found " + key
0202: + ", expecting one of: ");
0203: for (Iterator it = builtins.keySet().iterator(); it
0204: .hasNext();) {
0205: if (it.hasNext()) {
0206: buf.append(" ");
0207: } else {
0208: buf.append(" or ");
0209: }
0210: buf.append(it.next());
0211: if (it.hasNext()) {
0212: buf.append(", ");
0213: }
0214: }
0215: throw new ParseException(locationInfo + buf, target);
0216: }
0217: try {
0218: bi = (BuiltIn) bi.clone();
0219: } catch (CloneNotSupportedException e) {
0220: throw new InternalError();
0221: }
0222: bi.target = target;
0223: bi.key = key;
0224: return bi;
0225: }
0226:
0227: public String getCanonicalForm() {
0228: return target.getCanonicalForm() + "?" + key;
0229: }
0230:
0231: boolean isLiteral() {
0232: return false; // be on the safe side.
0233: }
0234:
0235: Expression _deepClone(String name, Expression subst) {
0236: try {
0237: BuiltIn clone = (BuiltIn) clone();
0238: clone.target = target.deepClone(name, subst);
0239: return clone;
0240: } catch (CloneNotSupportedException e) {
0241: throw new InternalError();
0242: }
0243: }
0244:
0245: static class lengthBI extends BuiltIn {
0246: TemplateModel _getAsTemplateModel(Environment env)
0247: throws TemplateException {
0248: return new SimpleNumber(target.getStringValue(env).length());
0249: }
0250: }
0251:
0252: static class dateBI extends BuiltIn {
0253: private final int dateType;
0254:
0255: dateBI(int dateType) {
0256: this .dateType = dateType;
0257: }
0258:
0259: TemplateModel _getAsTemplateModel(Environment env)
0260: throws TemplateException {
0261: TemplateModel model = target.getAsTemplateModel(env);
0262: if (model instanceof TemplateDateModel) {
0263: TemplateDateModel dmodel = (TemplateDateModel) model;
0264: int dtype = dmodel.getDateType();
0265: // Any date model can be coerced into its own type
0266: if (dateType == dtype) {
0267: return model;
0268: }
0269: // unknown and datetime can be coerced into any date type
0270: if (dtype == TemplateDateModel.UNKNOWN
0271: || dtype == TemplateDateModel.DATETIME) {
0272: return new SimpleDate(dmodel.getAsDate(), dateType);
0273: }
0274: throw new TemplateException("Cannot convert "
0275: + TemplateDateModel.TYPE_NAMES.get(dtype)
0276: + " into "
0277: + TemplateDateModel.TYPE_NAMES.get(dateType),
0278: env);
0279: }
0280: // Otherwise, interpret as a string and attempt
0281: // to parse it into a date.
0282: String s = target.getStringValue(env);
0283: return new DateParser(s, env);
0284: }
0285:
0286: private class DateParser implements TemplateDateModel,
0287: TemplateMethodModel, TemplateHashModel {
0288: private final String text;
0289: private final Environment env;
0290: private final DateFormat defaultFormat;
0291: private Date cachedValue;
0292:
0293: DateParser(String text, Environment env)
0294: throws TemplateModelException {
0295: this .text = text;
0296: this .env = env;
0297: this .defaultFormat = env.getDateFormatObject(dateType);
0298: }
0299:
0300: public Date getAsDate() throws TemplateModelException {
0301: if (cachedValue == null) {
0302: cachedValue = parse(defaultFormat);
0303: }
0304: return cachedValue;
0305: }
0306:
0307: public int getDateType() {
0308: return dateType;
0309: }
0310:
0311: public TemplateModel get(String pattern)
0312: throws TemplateModelException {
0313: return new SimpleDate(parse(env.getDateFormatObject(
0314: dateType, pattern)), dateType);
0315: }
0316:
0317: public Object exec(List arguments)
0318: throws TemplateModelException {
0319: if (arguments.size() != 1) {
0320: throw new TemplateModelException("string?" + key
0321: + "(...) requires exactly 1 argument.");
0322: }
0323: return get((String) arguments.get(0));
0324: }
0325:
0326: public boolean isEmpty() {
0327: return false;
0328: }
0329:
0330: private Date parse(DateFormat df)
0331: throws TemplateModelException {
0332: try {
0333: return df.parse(text);
0334: } catch (java.text.ParseException e) {
0335: String mess = "Error: " + getStartLocation()
0336: + "\nExpecting a date here, found: " + text;
0337: throw new TemplateModelException(mess);
0338: }
0339: }
0340: }
0341: }
0342:
0343: static class stringBI extends BuiltIn {
0344: TemplateModel _getAsTemplateModel(Environment env)
0345: throws TemplateException {
0346: TemplateModel model = target.getAsTemplateModel(env);
0347: if (model instanceof TemplateNumberModel) {
0348: return new NumberFormatter(EvaluationUtil.getNumber(
0349: (TemplateNumberModel) model, target, env), env);
0350: }
0351: if (model instanceof TemplateDateModel) {
0352: TemplateDateModel dm = (TemplateDateModel) model;
0353: int dateType = dm.getDateType();
0354: return new DateFormatter(EvaluationUtil.getDate(dm,
0355: target, env), dateType, env);
0356: }
0357: if (model instanceof SimpleScalar) {
0358: return model;
0359: }
0360: if (model instanceof TemplateBooleanModel) {
0361: return new BooleanFormatter(
0362: (TemplateBooleanModel) model, env);
0363: }
0364: if (model instanceof TemplateScalarModel) {
0365: return new SimpleScalar(((TemplateScalarModel) model)
0366: .getAsString());
0367: }
0368: throw invalidTypeException(model, target, env,
0369: "number, date, or string");
0370: }
0371:
0372: private static class NumberFormatter implements
0373: TemplateScalarModel, TemplateHashModel,
0374: TemplateMethodModel {
0375: private final Number number;
0376: private final Environment env;
0377: private final NumberFormat defaultFormat;
0378: private String cachedValue;
0379:
0380: NumberFormatter(Number number, Environment env) {
0381: this .number = number;
0382: this .env = env;
0383: defaultFormat = env.getNumberFormatObject(env
0384: .getNumberFormat());
0385: }
0386:
0387: public String getAsString() {
0388: if (cachedValue == null) {
0389: cachedValue = defaultFormat.format(number);
0390: }
0391: return cachedValue;
0392: }
0393:
0394: public TemplateModel get(String key) {
0395: return new SimpleScalar(env.getNumberFormatObject(key)
0396: .format(number));
0397: }
0398:
0399: public Object exec(List arguments)
0400: throws TemplateModelException {
0401: if (arguments.size() != 1) {
0402: throw new TemplateModelException(
0403: "number?string(...) requires exactly 1 argument.");
0404: }
0405: return get((String) arguments.get(0));
0406: }
0407:
0408: public boolean isEmpty() {
0409: return false;
0410: }
0411: }
0412:
0413: private static class DateFormatter implements
0414: TemplateScalarModel, TemplateHashModel,
0415: TemplateMethodModel {
0416: private final Date date;
0417: private final int dateType;
0418: private final Environment env;
0419: private final DateFormat defaultFormat;
0420: private String cachedValue;
0421:
0422: DateFormatter(Date date, int dateType, Environment env)
0423: throws TemplateModelException {
0424: this .date = date;
0425: this .dateType = dateType;
0426: this .env = env;
0427: defaultFormat = env.getDateFormatObject(dateType);
0428: }
0429:
0430: public String getAsString() throws TemplateModelException {
0431: if (dateType == TemplateDateModel.UNKNOWN) {
0432: throw new TemplateModelException(
0433: "Can't convert the date to string, because it is not known which parts of the date variable are in use. Use ?date, ?time or ?datetime built-in, or ?string.<format> or ?string(format) built-in with this date.");
0434: }
0435: if (cachedValue == null) {
0436: cachedValue = defaultFormat.format(date);
0437: }
0438: return cachedValue;
0439: }
0440:
0441: public TemplateModel get(String key)
0442: throws TemplateModelException {
0443: return new SimpleScalar(env.getDateFormatObject(
0444: dateType, key).format(date));
0445: }
0446:
0447: public Object exec(List arguments)
0448: throws TemplateModelException {
0449: if (arguments.size() != 1) {
0450: throw new TemplateModelException(
0451: "date?string(...) requires exactly 1 argument.");
0452: }
0453: return get((String) arguments.get(0));
0454: }
0455:
0456: public boolean isEmpty() {
0457: return false;
0458: }
0459: }
0460:
0461: private static class BooleanFormatter implements
0462: TemplateScalarModel, TemplateMethodModel {
0463: private final TemplateBooleanModel bool;
0464: private final Environment env;
0465:
0466: BooleanFormatter(TemplateBooleanModel bool, Environment env) {
0467: this .bool = bool;
0468: this .env = env;
0469: }
0470:
0471: public String getAsString() throws TemplateModelException {
0472: if (bool instanceof TemplateScalarModel) {
0473: return ((TemplateScalarModel) bool).getAsString();
0474: } else {
0475: return env.getBooleanFormat(bool.getAsBoolean());
0476: }
0477: }
0478:
0479: public Object exec(List arguments)
0480: throws TemplateModelException {
0481: if (arguments.size() != 2) {
0482: throw new TemplateModelException(
0483: "boolean?string(...) requires exactly "
0484: + "2 arguments.");
0485: }
0486: return new SimpleScalar((String) arguments.get(bool
0487: .getAsBoolean() ? 0 : 1));
0488: }
0489: }
0490: }
0491:
0492: static class trimBI extends StringBuiltIn {
0493: TemplateModel calculateResult(String s, Environment env) {
0494: return new SimpleScalar(s.trim());
0495: }
0496: }
0497:
0498: static class htmlBI extends StringBuiltIn {
0499: TemplateModel calculateResult(String s, Environment env) {
0500: return new SimpleScalar(StringUtil.HTMLEnc(s));
0501: }
0502: }
0503:
0504: static class xmlBI extends StringBuiltIn {
0505: TemplateModel calculateResult(String s, Environment env) {
0506: return new SimpleScalar(StringUtil.XMLEnc(s));
0507: }
0508: }
0509:
0510: static class rtfBI extends StringBuiltIn {
0511: TemplateModel calculateResult(String s, Environment env) {
0512: return new SimpleScalar(StringUtil.RTFEnc(s));
0513: }
0514: }
0515:
0516: static class urlBI extends StringBuiltIn {
0517:
0518: TemplateModel calculateResult(String s, Environment env) {
0519: return new urlBIResult(s, env);
0520: }
0521:
0522: static class urlBIResult implements TemplateScalarModel,
0523: TemplateMethodModel {
0524:
0525: private final String target;
0526: private final Environment env;
0527: private String cachedResult;
0528:
0529: private urlBIResult(String target, Environment env) {
0530: this .target = target;
0531: this .env = env;
0532: }
0533:
0534: public String getAsString() throws TemplateModelException {
0535: if (cachedResult == null) {
0536: String cs = env.getEffectiveURLEscapingCharset();
0537: if (cs == null) {
0538: throw new TemplateModelException(
0539: "To do URL encoding, the framework that encloses "
0540: + "FreeMarker must specify the output encoding "
0541: + "or the URL encoding charset, so ask the "
0542: + "programmers to fix it. Or, as a last chance, "
0543: + "you can set the url_encoding_charset setting in "
0544: + "the template, e.g. "
0545: + "<#setting url_escaping_charset='ISO-8859-1'>, or "
0546: + "give the charset explicitly to the buit-in, e.g. "
0547: + "foo?url('ISO-8859-1').");
0548: }
0549: try {
0550: cachedResult = StringUtil.URLEnc(target, cs);
0551: } catch (UnsupportedEncodingException e) {
0552: throw new TemplateModelException(
0553: "Failed to execute URL encoding.", e);
0554: }
0555: }
0556: return cachedResult;
0557: }
0558:
0559: public Object exec(List args) throws TemplateModelException {
0560: if (args.size() != 1) {
0561: throw new TemplateModelException(
0562: "The \"url\" built-in "
0563: + "needs exactly 1 parameter, the charset.");
0564: }
0565: try {
0566: return new SimpleScalar(StringUtil.URLEnc(target,
0567: (String) args.get(0)));
0568: } catch (UnsupportedEncodingException e) {
0569: throw new TemplateModelException(
0570: "Failed to execute URL encoding.", e);
0571: }
0572: }
0573:
0574: }
0575: }
0576:
0577: static class keysBI extends BuiltIn {
0578: TemplateModel _getAsTemplateModel(Environment env)
0579: throws TemplateException {
0580: TemplateModel model = target.getAsTemplateModel(env);
0581: if (model instanceof TemplateHashModelEx) {
0582: TemplateCollectionModel keys = ((TemplateHashModelEx) model)
0583: .keys();
0584: assertNonNull(keys, this , env);
0585: if (!(keys instanceof TemplateSequenceModel))
0586: keys = new CollectionAndSequence(keys);
0587: return keys;
0588: }
0589: throw invalidTypeException(model, target, env,
0590: "extended hash");
0591: }
0592: }
0593:
0594: static class valuesBI extends BuiltIn {
0595: TemplateModel _getAsTemplateModel(Environment env)
0596: throws TemplateException {
0597: TemplateModel model = target.getAsTemplateModel(env);
0598: if (model instanceof TemplateHashModelEx) {
0599: TemplateCollectionModel values = ((TemplateHashModelEx) model)
0600: .values();
0601: assertNonNull(values, this , env);
0602: if (!(values instanceof TemplateSequenceModel))
0603: values = new CollectionAndSequence(values);
0604: return values;
0605: }
0606: throw invalidTypeException(model, target, env,
0607: "extended hash");
0608: }
0609: }
0610:
0611: static class sizeBI extends BuiltIn {
0612: TemplateModel _getAsTemplateModel(Environment env)
0613: throws TemplateException {
0614: TemplateModel model = target.getAsTemplateModel(env);
0615: if (model instanceof TemplateSequenceModel) {
0616: int size = ((TemplateSequenceModel) model).size();
0617: return new SimpleNumber(size);
0618: }
0619: if (model instanceof TemplateHashModelEx) {
0620: int size = ((TemplateHashModelEx) model).size();
0621: return new SimpleNumber(size);
0622: }
0623: throw invalidTypeException(model, target, env,
0624: "extended-hash or sequence");
0625: }
0626: }
0627:
0628: static class existsBI extends BuiltIn {
0629: TemplateModel _getAsTemplateModel(Environment env)
0630: throws TemplateException {
0631: try {
0632: TemplateModel model = target.getAsTemplateModel(env);
0633: return model == null ? TemplateBooleanModel.FALSE
0634: : TemplateBooleanModel.TRUE;
0635: } catch (InvalidReferenceException ire) {
0636: if (target instanceof ParentheticalExpression) {
0637: return TemplateBooleanModel.FALSE;
0638: }
0639: throw ire;
0640: }
0641: }
0642:
0643: boolean isTrue(Environment env) throws TemplateException {
0644: return _getAsTemplateModel(env) == TemplateBooleanModel.TRUE;
0645: }
0646: }
0647:
0648: static class has_contentBI extends BuiltIn {
0649: TemplateModel _getAsTemplateModel(Environment env)
0650: throws TemplateException {
0651: try {
0652: TemplateModel model = target.getAsTemplateModel(env);
0653: return Expression.isEmpty(model) ? TemplateBooleanModel.FALSE
0654: : TemplateBooleanModel.TRUE;
0655: } catch (InvalidReferenceException ire) {
0656: if (target instanceof ParentheticalExpression) {
0657: return TemplateBooleanModel.FALSE;
0658: }
0659: throw ire;
0660: }
0661: }
0662:
0663: boolean isTrue(Environment env) throws TemplateException {
0664: return _getAsTemplateModel(env) == TemplateBooleanModel.TRUE;
0665: }
0666: }
0667:
0668: static class if_existsBI extends BuiltIn {
0669: TemplateModel _getAsTemplateModel(Environment env)
0670: throws TemplateException {
0671: try {
0672: TemplateModel model = target.getAsTemplateModel(env);
0673: return model == null ? TemplateModel.NOTHING : model;
0674: } catch (InvalidReferenceException ire) {
0675: if (target instanceof ParentheticalExpression) {
0676: return TemplateModel.NOTHING;
0677: }
0678: throw ire;
0679: }
0680: }
0681: }
0682:
0683: static class is_stringBI extends BuiltIn {
0684: TemplateModel _getAsTemplateModel(Environment env)
0685: throws TemplateException {
0686: TemplateModel tm = target.getAsTemplateModel(env);
0687: assertNonNull(tm, target, env);
0688: return (tm instanceof TemplateScalarModel) ? TemplateBooleanModel.TRUE
0689: : TemplateBooleanModel.FALSE;
0690: }
0691: }
0692:
0693: static class is_numberBI extends BuiltIn {
0694: TemplateModel _getAsTemplateModel(Environment env)
0695: throws TemplateException {
0696: TemplateModel tm = target.getAsTemplateModel(env);
0697: assertNonNull(tm, target, env);
0698: return (tm instanceof TemplateNumberModel) ? TemplateBooleanModel.TRUE
0699: : TemplateBooleanModel.FALSE;
0700: }
0701: }
0702:
0703: static class is_nodeBI extends BuiltIn {
0704: TemplateModel _getAsTemplateModel(Environment env)
0705: throws TemplateException {
0706: TemplateModel tm = target.getAsTemplateModel(env);
0707: assertNonNull(tm, target, env);
0708: return (tm instanceof TemplateNodeModel) ? TemplateBooleanModel.TRUE
0709: : TemplateBooleanModel.FALSE;
0710: }
0711: }
0712:
0713: static class is_booleanBI extends BuiltIn {
0714: TemplateModel _getAsTemplateModel(Environment env)
0715: throws TemplateException {
0716: TemplateModel tm = target.getAsTemplateModel(env);
0717: assertNonNull(tm, target, env);
0718: return (tm instanceof TemplateBooleanModel) ? TemplateBooleanModel.TRUE
0719: : TemplateBooleanModel.FALSE;
0720: }
0721: }
0722:
0723: static class is_dateBI extends BuiltIn {
0724: TemplateModel _getAsTemplateModel(Environment env)
0725: throws TemplateException {
0726: TemplateModel tm = target.getAsTemplateModel(env);
0727: assertNonNull(tm, target, env);
0728: return (tm instanceof TemplateDateModel) ? TemplateBooleanModel.TRUE
0729: : TemplateBooleanModel.FALSE;
0730: }
0731: }
0732:
0733: static class is_methodBI extends BuiltIn {
0734: TemplateModel _getAsTemplateModel(Environment env)
0735: throws TemplateException {
0736: TemplateModel tm = target.getAsTemplateModel(env);
0737: assertNonNull(tm, target, env);
0738: return (tm instanceof TemplateMethodModel) ? TemplateBooleanModel.TRUE
0739: : TemplateBooleanModel.FALSE;
0740: }
0741: }
0742:
0743: static class is_macroBI extends BuiltIn {
0744: TemplateModel _getAsTemplateModel(Environment env)
0745: throws TemplateException {
0746: TemplateModel tm = target.getAsTemplateModel(env);
0747: assertNonNull(tm, target, env);
0748: return (tm instanceof Macro) ? TemplateBooleanModel.TRUE
0749: : TemplateBooleanModel.FALSE;
0750: }
0751: }
0752:
0753: static class is_transformBI extends BuiltIn {
0754: TemplateModel _getAsTemplateModel(Environment env)
0755: throws TemplateException {
0756: TemplateModel tm = target.getAsTemplateModel(env);
0757: assertNonNull(tm, target, env);
0758: return (tm instanceof TemplateTransformModel) ? TemplateBooleanModel.TRUE
0759: : TemplateBooleanModel.FALSE;
0760: }
0761: }
0762:
0763: static class is_hashBI extends BuiltIn {
0764: TemplateModel _getAsTemplateModel(Environment env)
0765: throws TemplateException {
0766: TemplateModel tm = target.getAsTemplateModel(env);
0767: assertNonNull(tm, target, env);
0768: return (tm instanceof TemplateHashModel) ? TemplateBooleanModel.TRUE
0769: : TemplateBooleanModel.FALSE;
0770: }
0771: }
0772:
0773: static class is_hash_exBI extends BuiltIn {
0774: TemplateModel _getAsTemplateModel(Environment env)
0775: throws TemplateException {
0776: TemplateModel tm = target.getAsTemplateModel(env);
0777: assertNonNull(tm, target, env);
0778: return (tm instanceof TemplateHashModelEx) ? TemplateBooleanModel.TRUE
0779: : TemplateBooleanModel.FALSE;
0780: }
0781: }
0782:
0783: static class is_sequenceBI extends BuiltIn {
0784: TemplateModel _getAsTemplateModel(Environment env)
0785: throws TemplateException {
0786: TemplateModel tm = target.getAsTemplateModel(env);
0787: assertNonNull(tm, target, env);
0788: return (tm instanceof TemplateSequenceModel) ? TemplateBooleanModel.TRUE
0789: : TemplateBooleanModel.FALSE;
0790: }
0791: }
0792:
0793: static class is_collectionBI extends BuiltIn {
0794: TemplateModel _getAsTemplateModel(Environment env)
0795: throws TemplateException {
0796: TemplateModel tm = target.getAsTemplateModel(env);
0797: assertNonNull(tm, target, env);
0798: return (tm instanceof TemplateCollectionModel) ? TemplateBooleanModel.TRUE
0799: : TemplateBooleanModel.FALSE;
0800: }
0801: }
0802:
0803: static class is_indexableBI extends BuiltIn {
0804: TemplateModel _getAsTemplateModel(Environment env)
0805: throws TemplateException {
0806: TemplateModel tm = target.getAsTemplateModel(env);
0807: assertNonNull(tm, target, env);
0808: return (tm instanceof TemplateSequenceModel) ? TemplateBooleanModel.TRUE
0809: : TemplateBooleanModel.FALSE;
0810: }
0811: }
0812:
0813: static class is_enumerableBI extends BuiltIn {
0814: TemplateModel _getAsTemplateModel(Environment env)
0815: throws TemplateException {
0816: TemplateModel tm = target.getAsTemplateModel(env);
0817: assertNonNull(tm, target, env);
0818: return (tm instanceof TemplateSequenceModel || tm instanceof TemplateCollectionModel) ? TemplateBooleanModel.TRUE
0819: : TemplateBooleanModel.FALSE;
0820: }
0821: }
0822:
0823: static class is_directiveBI extends BuiltIn {
0824: TemplateModel _getAsTemplateModel(Environment env)
0825: throws TemplateException {
0826: TemplateModel tm = target.getAsTemplateModel(env);
0827: assertNonNull(tm, target, env);
0828: return (tm instanceof TemplateTransformModel || tm instanceof Macro) ? TemplateBooleanModel.TRUE
0829: : TemplateBooleanModel.FALSE;
0830: }
0831: }
0832:
0833: static class namespaceBI extends BuiltIn {
0834: TemplateModel _getAsTemplateModel(Environment env)
0835: throws TemplateException {
0836: TemplateModel tm = target.getAsTemplateModel(env);
0837: if (!(tm instanceof Macro)) {
0838: invalidTypeException(tm, target, env, "macro");
0839: }
0840: return env.getMacroNamespace((Macro) tm);
0841: }
0842: }
0843:
0844: static class defaultBI extends BuiltIn {
0845: TemplateModel _getAsTemplateModel(final Environment env)
0846: throws TemplateException {
0847: try {
0848: TemplateModel model = target.getAsTemplateModel(env);
0849: return model == null ? FIRST_NON_NULL_METHOD
0850: : new ConstantMethod(model);
0851: } catch (InvalidReferenceException ire) {
0852: if (target instanceof ParentheticalExpression) {
0853: return FIRST_NON_NULL_METHOD;
0854: }
0855: throw ire;
0856: }
0857: }
0858:
0859: private static class ConstantMethod implements
0860: TemplateMethodModelEx {
0861: private final TemplateModel constant;
0862:
0863: ConstantMethod(TemplateModel constant) {
0864: this .constant = constant;
0865: }
0866:
0867: public Object exec(List args) {
0868: return constant;
0869: }
0870: }
0871:
0872: /**
0873: * A method that goes through the arguments one by one and returns
0874: * the first one that is non-null. If all args are null, returns null.
0875: */
0876: private static final TemplateMethodModelEx FIRST_NON_NULL_METHOD = new TemplateMethodModelEx() {
0877: public Object exec(List args) throws TemplateModelException {
0878: if (args.isEmpty()) {
0879: throw new TemplateModelException(
0880: "?default(arg) expects at least one argument.");
0881: }
0882: TemplateModel result = null;
0883: for (int i = 0; i < args.size(); i++) {
0884: result = (TemplateModel) args.get(i);
0885: if (result != null) {
0886: break;
0887: }
0888: }
0889: return result;
0890: }
0891: };
0892: }
0893:
0894: static class containsBI extends BuiltIn {
0895: TemplateModel _getAsTemplateModel(Environment env)
0896: throws TemplateException {
0897: TemplateModel model = target.getAsTemplateModel(env);
0898: if (model instanceof TemplateScalarModel) {
0899: return new BIMethod(((TemplateScalarModel) model)
0900: .getAsString());
0901: }
0902: throw invalidTypeException(model, target, env, "string");
0903: }
0904:
0905: private class BIMethod implements TemplateMethodModelEx {
0906: private String s;
0907:
0908: private BIMethod(String s) {
0909: this .s = s;
0910: }
0911:
0912: public Object exec(List args) throws TemplateModelException {
0913: Object obj;
0914: String sub;
0915:
0916: int ln = args.size();
0917: if (ln != 1) {
0918: throw new TemplateModelException(
0919: "?contains(...) expects one argument.");
0920: }
0921:
0922: obj = args.get(0);
0923: if (!(obj instanceof TemplateScalarModel)) {
0924: throw new TemplateModelException(
0925: "?contains(...) expects a string as "
0926: + "its first argument.");
0927: }
0928: sub = ((TemplateScalarModel) obj).getAsString();
0929:
0930: return (s.indexOf(sub) != -1) ? TemplateBooleanModel.TRUE
0931: : TemplateBooleanModel.FALSE;
0932: }
0933: }
0934: }
0935:
0936: static class index_ofBI extends BuiltIn {
0937: TemplateModel _getAsTemplateModel(Environment env)
0938: throws TemplateException {
0939: TemplateModel model = target.getAsTemplateModel(env);
0940: if (model instanceof TemplateScalarModel) {
0941: return new BIMethod(((TemplateScalarModel) model)
0942: .getAsString());
0943: }
0944: throw invalidTypeException(model, target, env, "string");
0945: }
0946:
0947: private class BIMethod implements TemplateMethodModelEx {
0948: private String s;
0949:
0950: private BIMethod(String s) {
0951: this .s = s;
0952: }
0953:
0954: public Object exec(List args) throws TemplateModelException {
0955: Object obj;
0956: String sub;
0957: int fidx;
0958:
0959: int ln = args.size();
0960: if (ln == 0) {
0961: throw new TemplateModelException(
0962: "?index_of(...) expects at least one argument.");
0963: }
0964: if (ln > 2) {
0965: throw new TemplateModelException(
0966: "?index_of(...) expects at most two arguments.");
0967: }
0968:
0969: obj = args.get(0);
0970: if (!(obj instanceof TemplateScalarModel)) {
0971: throw new TemplateModelException(
0972: "?index_of(...) expects a string as "
0973: + "its first argument.");
0974: }
0975: sub = ((TemplateScalarModel) obj).getAsString();
0976:
0977: if (ln > 1) {
0978: obj = args.get(1);
0979: if (!(obj instanceof TemplateNumberModel)) {
0980: throw new TemplateModelException(
0981: "?index_of(...) expects a number as "
0982: + "its second argument.");
0983: }
0984: fidx = ((TemplateNumberModel) obj).getAsNumber()
0985: .intValue();
0986: } else {
0987: fidx = 0;
0988: }
0989:
0990: return new SimpleNumber(s.indexOf(sub, fidx));
0991: }
0992: }
0993: }
0994:
0995: static class last_index_ofBI extends BuiltIn {
0996: TemplateModel _getAsTemplateModel(Environment env)
0997: throws TemplateException {
0998: TemplateModel model = target.getAsTemplateModel(env);
0999: if (model instanceof TemplateScalarModel) {
1000: return new BIMethod(((TemplateScalarModel) model)
1001: .getAsString());
1002: }
1003: throw invalidTypeException(model, target, env, "string");
1004: }
1005:
1006: private class BIMethod implements TemplateMethodModelEx {
1007: private String s;
1008:
1009: private BIMethod(String s) {
1010: this .s = s;
1011: }
1012:
1013: public Object exec(List args) throws TemplateModelException {
1014: Object obj;
1015: String sub;
1016:
1017: int ln = args.size();
1018: if (ln == 0) {
1019: throw new TemplateModelException(
1020: "?last_index_of(...) expects at least one argument.");
1021: }
1022: if (ln > 2) {
1023: throw new TemplateModelException(
1024: "?last_index_of(...) expects at most two arguments.");
1025: }
1026:
1027: obj = args.get(0);
1028: if (!(obj instanceof TemplateScalarModel)) {
1029: throw new TemplateModelException(
1030: "?last_index_of(...) expects a string as "
1031: + "its first argument.");
1032: }
1033: sub = ((TemplateScalarModel) obj).getAsString();
1034:
1035: if (ln > 1) {
1036: obj = args.get(1);
1037: if (!(obj instanceof TemplateNumberModel)) {
1038: throw new TemplateModelException(
1039: "?last_index_of(...) expects a number as "
1040: + "its second argument.");
1041: }
1042: int fidx = ((TemplateNumberModel) obj)
1043: .getAsNumber().intValue();
1044: return new SimpleNumber(s.lastIndexOf(sub, fidx));
1045: } else {
1046: return new SimpleNumber(s.lastIndexOf(sub));
1047: }
1048: }
1049: }
1050: }
1051:
1052: static class starts_withBI extends BuiltIn {
1053: TemplateModel _getAsTemplateModel(Environment env)
1054: throws TemplateException {
1055: TemplateModel model = target.getAsTemplateModel(env);
1056: if (model instanceof TemplateScalarModel) {
1057: return new BIMethod(((TemplateScalarModel) model)
1058: .getAsString());
1059: }
1060: throw invalidTypeException(model, target, env, "string");
1061: }
1062:
1063: private class BIMethod implements TemplateMethodModelEx {
1064: private String s;
1065:
1066: private BIMethod(String s) {
1067: this .s = s;
1068: }
1069:
1070: public Object exec(List args) throws TemplateModelException {
1071: String sub;
1072:
1073: if (args.size() != 1) {
1074: throw new TemplateModelException(
1075: "?starts_with(...) expects exactly 1 argument.");
1076: }
1077:
1078: Object obj = args.get(0);
1079: if (!(obj instanceof TemplateScalarModel)) {
1080: throw new TemplateModelException(
1081: "?starts_with(...) expects a string argument");
1082: }
1083: sub = ((TemplateScalarModel) obj).getAsString();
1084:
1085: return s.startsWith(sub) ? TemplateBooleanModel.TRUE
1086: : TemplateBooleanModel.FALSE;
1087: }
1088: }
1089: }
1090:
1091: static class ends_withBI extends BuiltIn {
1092: TemplateModel _getAsTemplateModel(Environment env)
1093: throws TemplateException {
1094: TemplateModel model = target.getAsTemplateModel(env);
1095: if (model instanceof TemplateScalarModel) {
1096: return new BIMethod(((TemplateScalarModel) model)
1097: .getAsString());
1098: }
1099: throw invalidTypeException(model, target, env, "string");
1100: }
1101:
1102: private class BIMethod implements TemplateMethodModelEx {
1103: private String s;
1104:
1105: private BIMethod(String s) {
1106: this .s = s;
1107: }
1108:
1109: public Object exec(List args) throws TemplateModelException {
1110: String sub;
1111:
1112: if (args.size() != 1) {
1113: throw new TemplateModelException(
1114: "?ends_with(...) expects exactly 1 argument.");
1115: }
1116:
1117: Object obj = args.get(0);
1118: if (!(obj instanceof TemplateScalarModel)) {
1119: throw new TemplateModelException(
1120: "?ends_with(...) expects a string argument");
1121: }
1122: sub = ((TemplateScalarModel) obj).getAsString();
1123:
1124: return s.endsWith(sub) ? TemplateBooleanModel.TRUE
1125: : TemplateBooleanModel.FALSE;
1126: }
1127: }
1128: }
1129:
1130: static class replaceBI extends BuiltIn {
1131: TemplateModel _getAsTemplateModel(Environment env)
1132: throws TemplateException {
1133: TemplateModel model = target.getAsTemplateModel(env);
1134: if (model instanceof TemplateScalarModel) {
1135: return new BIMethod(((TemplateScalarModel) model)
1136: .getAsString());
1137: }
1138: throw invalidTypeException(model, target, env, "string");
1139: }
1140:
1141: private class BIMethod implements TemplateMethodModel {
1142: private String s;
1143:
1144: private BIMethod(String s) {
1145: this .s = s;
1146: }
1147:
1148: public Object exec(List args) throws TemplateModelException {
1149: int numArgs = args.size();
1150: if (numArgs < 2 || numArgs > 3) {
1151: throw new TemplateModelException(
1152: "?replace(...) needs 2 or 3 arguments.");
1153: }
1154: String first = (String) args.get(0);
1155: String second = (String) args.get(1);
1156: String flags = numArgs > 2 ? (String) args.get(2) : "";
1157: boolean caseInsensitive = flags.indexOf('i') >= 0;
1158: boolean firstOnly = flags.indexOf('f') >= 0;
1159: if (flags.indexOf('r') >= 0) {
1160: throw new TemplateModelException(
1161: "The regular expression classes are not available.");
1162: }
1163: return new SimpleScalar(StringUtil.replace(s, first,
1164: second, caseInsensitive, firstOnly));
1165: }
1166: }
1167: }
1168:
1169: static class splitBI extends BuiltIn {
1170: TemplateModel _getAsTemplateModel(Environment env)
1171: throws TemplateException {
1172: TemplateModel model = target.getAsTemplateModel(env);
1173: if (model instanceof TemplateScalarModel) {
1174: return new BIMethod(((TemplateScalarModel) model)
1175: .getAsString());
1176: }
1177: throw invalidTypeException(model, target, env, "string");
1178: }
1179:
1180: private class BIMethod implements TemplateMethodModel {
1181: private String s;
1182:
1183: private BIMethod(String s) {
1184: this .s = s;
1185: }
1186:
1187: public Object exec(List args) throws TemplateModelException {
1188: int numArgs = args.size();
1189: if (numArgs != 1 && numArgs != 2) {
1190: throw new TemplateModelException(
1191: "?split(...) expects 1 or 2 arguments.");
1192: }
1193: String splitString = (String) args.get(0);
1194: String flags = numArgs == 2 ? (String) args.get(1) : "";
1195: boolean caseInsensitive = flags.indexOf('i') >= 0;
1196: if (flags.indexOf('r') >= 0) {
1197: throw new TemplateModelException(
1198: "regular expression classes not available");
1199: }
1200: return new StringArraySequence(StringUtil.split(s,
1201: splitString, caseInsensitive));
1202: }
1203: }
1204: }
1205:
1206: static class left_padBI extends BuiltIn {
1207: TemplateModel _getAsTemplateModel(Environment env)
1208: throws TemplateException {
1209: TemplateModel model = target.getAsTemplateModel(env);
1210: if (model instanceof TemplateScalarModel) {
1211: return new BIMethod(((TemplateScalarModel) model)
1212: .getAsString());
1213: }
1214: throw invalidTypeException(model, target, env, "string");
1215: }
1216:
1217: private class BIMethod implements TemplateMethodModelEx {
1218: private String s;
1219:
1220: private BIMethod(String s) {
1221: this .s = s;
1222: }
1223:
1224: public Object exec(List args) throws TemplateModelException {
1225: Object obj;
1226:
1227: int ln = args.size();
1228: if (ln == 0) {
1229: throw new TemplateModelException(
1230: "?left_pad(...) expects at least 1 argument.");
1231: }
1232: if (ln > 2) {
1233: throw new TemplateModelException(
1234: "?left_pad(...) expects at most 2 arguments.");
1235: }
1236:
1237: obj = args.get(0);
1238: if (!(obj instanceof TemplateNumberModel)) {
1239: throw new TemplateModelException(
1240: "?left_pad(...) expects a number as "
1241: + "its 1st argument.");
1242: }
1243: int width = ((TemplateNumberModel) obj).getAsNumber()
1244: .intValue();
1245:
1246: if (ln > 1) {
1247: obj = args.get(1);
1248: if (!(obj instanceof TemplateScalarModel)) {
1249: throw new TemplateModelException(
1250: "?left_pad(...) expects a string as "
1251: + "its 2nd argument.");
1252: }
1253: String filling = ((TemplateScalarModel) obj)
1254: .getAsString();
1255: try {
1256: return new SimpleScalar(StringUtil.leftPad(s,
1257: width, filling));
1258: } catch (IllegalArgumentException e) {
1259: if (filling.length() == 0) {
1260: throw new TemplateModelException(
1261: "The 2nd argument of ?left_pad(...) "
1262: + "can't be a 0 length string.");
1263: } else {
1264: throw new TemplateModelException(
1265: "Error while executing the ?left_pad(...) "
1266: + "built-in.", e);
1267: }
1268: }
1269: } else {
1270: return new SimpleScalar(StringUtil
1271: .leftPad(s, width));
1272: }
1273: }
1274: }
1275: }
1276:
1277: static class right_padBI extends BuiltIn {
1278: TemplateModel _getAsTemplateModel(Environment env)
1279: throws TemplateException {
1280: TemplateModel model = target.getAsTemplateModel(env);
1281: if (model instanceof TemplateScalarModel) {
1282: return new BIMethod(((TemplateScalarModel) model)
1283: .getAsString());
1284: }
1285: throw invalidTypeException(model, target, env, "string");
1286: }
1287:
1288: private class BIMethod implements TemplateMethodModelEx {
1289: private String s;
1290:
1291: private BIMethod(String s) {
1292: this .s = s;
1293: }
1294:
1295: public Object exec(List args) throws TemplateModelException {
1296: Object obj;
1297:
1298: int ln = args.size();
1299: if (ln == 0) {
1300: throw new TemplateModelException(
1301: "?right_pad(...) expects at least 1 argument.");
1302: }
1303: if (ln > 2) {
1304: throw new TemplateModelException(
1305: "?right_pad(...) expects at most 2 arguments.");
1306: }
1307:
1308: obj = args.get(0);
1309: if (!(obj instanceof TemplateNumberModel)) {
1310: throw new TemplateModelException(
1311: "?right_pad(...) expects a number as "
1312: + "its 1st argument.");
1313: }
1314: int width = ((TemplateNumberModel) obj).getAsNumber()
1315: .intValue();
1316:
1317: if (ln > 1) {
1318: obj = args.get(1);
1319: if (!(obj instanceof TemplateScalarModel)) {
1320: throw new TemplateModelException(
1321: "?right_pad(...) expects a string as "
1322: + "its 2nd argument.");
1323: }
1324: String filling = ((TemplateScalarModel) obj)
1325: .getAsString();
1326: try {
1327: return new SimpleScalar(StringUtil.rightPad(s,
1328: width, filling));
1329: } catch (IllegalArgumentException e) {
1330: if (filling.length() == 0) {
1331: throw new TemplateModelException(
1332: "The 2nd argument of ?right_pad(...) "
1333: + "can't be a 0 length string.");
1334: } else {
1335: throw new TemplateModelException(
1336: "Error while executing the ?right_pad(...) "
1337: + "built-in.", e);
1338: }
1339: }
1340: } else {
1341: return new SimpleScalar(StringUtil.rightPad(s,
1342: width));
1343: }
1344: }
1345: }
1346: }
1347:
1348: }
|