001: /*
002: * Copyright (c) 2003 The Visigoth Software Society. All rights
003: * reserved.
004: *
005: * Redistribution and use in source and binary forms, with or without
006: * modification, are permitted provided that the following conditions
007: * are met:
008: *
009: * 1. Redistributions of source code must retain the above copyright
010: * notice, this list of conditions and the following disclaimer.
011: *
012: * 2. Redistributions in binary form must reproduce the above copyright
013: * notice, this list of conditions and the following disclaimer in
014: * the documentation and/or other materials provided with the
015: * distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowledgement:
019: * "This product includes software developed by the
020: * Visigoth Software Society (http://www.visigoths.org/)."
021: * Alternately, this acknowledgement may appear in the software itself,
022: * if and wherever such third-party acknowledgements normally appear.
023: *
024: * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
025: * project contributors may be used to endorse or promote products derived
026: * from this software without prior written permission. For written
027: * permission, please contact visigoths@visigoths.org.
028: *
029: * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
030: * nor may "FreeMarker" or "Visigoth" appear in their names
031: * without prior written permission of the Visigoth Software Society.
032: *
033: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
034: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
035: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
036: * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
037: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
038: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
039: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
040: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
041: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
042: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
043: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
044: * SUCH DAMAGE.
045: * ====================================================================
046: *
047: * This software consists of voluntary contributions made by many
048: * individuals on behalf of the Visigoth Software Society. For more
049: * information on the Visigoth Software Society, please see
050: * http://www.visigoths.org/
051: */
052:
053: package freemarker.core;
054:
055: import freemarker.template.*;
056: import java.util.*;
057:
058: /**
059: * An operator for the + operator. Note that this is treated
060: * separately from the other 4 arithmetic operators,
061: * since + is overloaded to mean string concatenation.
062: * @author <a href="mailto:jon@revusky.com">Jonathan Revusky</a>
063: */
064: final class AddConcatExpression extends Expression {
065:
066: private final Expression left;
067: private final Expression right;
068:
069: AddConcatExpression(Expression left, Expression right) {
070: this .left = left;
071: this .right = right;
072: }
073:
074: TemplateModel _getAsTemplateModel(Environment env)
075: throws TemplateException {
076: TemplateModel leftModel = left.getAsTemplateModel(env);
077: TemplateModel rightModel = right.getAsTemplateModel(env);
078: if (leftModel instanceof TemplateNumberModel
079: && rightModel instanceof TemplateNumberModel) {
080: Number first = EvaluationUtil.getNumber(
081: (TemplateNumberModel) leftModel, left, env);
082: Number second = EvaluationUtil.getNumber(
083: (TemplateNumberModel) rightModel, right, env);
084: ArithmeticEngine ae = env != null ? env
085: .getArithmeticEngine() : getTemplate()
086: .getArithmeticEngine();
087: return new SimpleNumber(ae.add(first, second));
088: } else if (leftModel instanceof TemplateSequenceModel
089: && rightModel instanceof TemplateSequenceModel) {
090: return new ConcatenatedSequence(
091: (TemplateSequenceModel) leftModel,
092: (TemplateSequenceModel) rightModel);
093: } else {
094: try {
095: String s1 = getStringValue(leftModel, left, env);
096: if (s1 == null)
097: s1 = "null";
098: String s2 = getStringValue(rightModel, right, env);
099: if (s2 == null)
100: s2 = "null";
101: return new SimpleScalar(s1.concat(s2));
102: } catch (NonStringException e) {
103: if (leftModel instanceof TemplateHashModel
104: && rightModel instanceof TemplateHashModel) {
105: if (leftModel instanceof TemplateHashModelEx
106: && rightModel instanceof TemplateHashModelEx) {
107: TemplateHashModelEx leftModelEx = (TemplateHashModelEx) leftModel;
108: TemplateHashModelEx rightModelEx = (TemplateHashModelEx) rightModel;
109: if (leftModelEx.size() == 0) {
110: return rightModelEx;
111: } else if (rightModelEx.size() == 0) {
112: return leftModelEx;
113: } else {
114: return new ConcatenatedHashEx(leftModelEx,
115: rightModelEx);
116: }
117: } else {
118: return new ConcatenatedHash(
119: (TemplateHashModel) leftModel,
120: (TemplateHashModel) rightModel);
121: }
122: } else {
123: throw e;
124: }
125: }
126: }
127: }
128:
129: boolean isLiteral() {
130: return constantValue != null
131: || (left.isLiteral() && right.isLiteral());
132: }
133:
134: Expression _deepClone(String name, Expression subst) {
135: return new AddConcatExpression(left.deepClone(name, subst),
136: right.deepClone(name, subst));
137: }
138:
139: public String getCanonicalForm() {
140: return left.getCanonicalForm() + " + "
141: + right.getCanonicalForm();
142: }
143:
144: private static final class ConcatenatedSequence implements
145: TemplateSequenceModel {
146: private final TemplateSequenceModel left;
147: private final TemplateSequenceModel right;
148:
149: ConcatenatedSequence(TemplateSequenceModel left,
150: TemplateSequenceModel right) {
151: this .left = left;
152: this .right = right;
153: }
154:
155: public int size() throws TemplateModelException {
156: return left.size() + right.size();
157: }
158:
159: public TemplateModel get(int i) throws TemplateModelException {
160: int ls = left.size();
161: return i < ls ? left.get(i) : right.get(i - ls);
162: }
163: }
164:
165: private static class ConcatenatedHash implements TemplateHashModel {
166: protected final TemplateHashModel left;
167: protected final TemplateHashModel right;
168:
169: ConcatenatedHash(TemplateHashModel left, TemplateHashModel right) {
170: this .left = left;
171: this .right = right;
172: }
173:
174: public TemplateModel get(String key)
175: throws TemplateModelException {
176: TemplateModel model = right.get(key);
177: return (model != null) ? model : left.get(key);
178: }
179:
180: public boolean isEmpty() throws TemplateModelException {
181: return left.isEmpty() && right.isEmpty();
182: }
183: }
184:
185: private static final class ConcatenatedHashEx extends
186: ConcatenatedHash implements TemplateHashModelEx {
187: private Set keySet;
188: private TemplateCollectionModel keys;
189: private TemplateCollectionModel values;
190: private int size;
191:
192: ConcatenatedHashEx(TemplateHashModelEx left,
193: TemplateHashModelEx right) {
194: super (left, right);
195: }
196:
197: public int size() throws TemplateModelException {
198: initKeys();
199: return size;
200: }
201:
202: public TemplateCollectionModel keys()
203: throws TemplateModelException {
204: initKeys();
205: return keys;
206: }
207:
208: public TemplateCollectionModel values()
209: throws TemplateModelException {
210: initValues();
211: return values;
212: }
213:
214: private void initKeys() throws TemplateModelException {
215: if (keys == null) {
216: keySet = new HashSet();
217: addKeys(keySet, (TemplateHashModelEx) this .left);
218: addKeys(keySet, (TemplateHashModelEx) this .right);
219: size = keySet.size();
220: keys = new CollectionAndSequence(new SimpleSequence(
221: keySet));
222: }
223: }
224:
225: private static void addKeys(Set set, TemplateHashModelEx hash)
226: throws TemplateModelException {
227: TemplateModelIterator it = hash.keys().iterator();
228: while (it.hasNext()) {
229: set
230: .add(((TemplateScalarModel) it.next())
231: .getAsString());
232: }
233: }
234:
235: private void initValues() throws TemplateModelException {
236: if (values == null) {
237: List list = new ArrayList(size());
238: for (Iterator it = keySet.iterator(); it.hasNext();) {
239: list.add(get((String) it.next()));
240: }
241: values = new CollectionAndSequence(new SimpleSequence(
242: list));
243: }
244: }
245: }
246: }
|