1 /*
2 * Copyright (c) 2003 The Visigoth Software Society. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The end-user documentation included with the redistribution, if
18 * any, must include the following acknowledgement:
19 * "This product includes software developed by the
20 * Visigoth Software Society (http://www.visigoths.org/)."
21 * Alternately, this acknowledgement may appear in the software itself,
22 * if and wherever such third-party acknowledgements normally appear.
23 *
24 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25 * project contributors may be used to endorse or promote products derived
26 * from this software without prior written permission. For written
27 * permission, please contact visigoths@visigoths.org.
28 *
29 * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30 * nor may "FreeMarker" or "Visigoth" appear in their names
31 * without prior written permission of the Visigoth Software Society.
32 *
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36 * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 * ====================================================================
46 *
47 * This software consists of voluntary contributions made by many
48 * individuals on behalf of the Visigoth Software Society. For more
49 * information on the Visigoth Software Society, please see
50 * http://www.visigoths.org/
51 */
52
53 package freemarker.core;
54
55 import java.util.ArrayList;
56 import freemarker.template;
57
58 /**
59 * A unary operator that uses the string value of an expression as a hash key.
60 * It associates with the <tt>Identifier</tt> or <tt>Dot</tt> to its left.
61 */
62 final class DynamicKeyName extends Expression {
63
64 private final Expression nameExpression;
65 private final Expression target;
66
67 DynamicKeyName(Expression target, Expression nameExpression) {
68 this.target = target;
69 this.nameExpression = nameExpression;
70 }
71
72 TemplateModel _getAsTemplateModel(Environment env) throws TemplateException
73 {
74 TemplateModel targetModel = target.getAsTemplateModel(env);
75 assertNonNull(targetModel, target, env);
76 if (nameExpression instanceof Range) {
77 return dealWithRangeKey(targetModel, (Range) nameExpression, env);
78 }
79 TemplateModel keyModel = nameExpression.getAsTemplateModel(env);
80 if(keyModel == null) {
81 if(env.isClassicCompatible()) {
82 keyModel = TemplateScalarModel.EMPTY_STRING;
83 }
84 else {
85 assertNonNull(keyModel, nameExpression, env);
86 }
87 }
88 if (keyModel instanceof TemplateNumberModel) {
89 int index = EvaluationUtil.getNumber(keyModel, nameExpression, env).intValue();
90 return dealWithNumericalKey(targetModel, index, env);
91 }
92 if (keyModel instanceof TemplateScalarModel) {
93 String key = EvaluationUtil.getString((TemplateScalarModel)keyModel, nameExpression, env);
94 return dealWithStringKey(targetModel, key, env);
95 }
96 throw invalidTypeException(keyModel, nameExpression, env, "number, range, or string");
97 }
98
99
100 private TemplateModel dealWithNumericalKey(TemplateModel targetModel,
101 int index,
102 Environment env)
103 throws TemplateException
104 {
105 if (targetModel instanceof TemplateSequenceModel) {
106 TemplateSequenceModel tsm = (TemplateSequenceModel) targetModel;
107 int size = Integer.MAX_VALUE;
108 try {
109 size = tsm.size();
110 } catch (Exception e) {}
111 return index<size ? tsm.get(index) : null;
112 }
113
114 try
115 {
116 String s = target.getStringValue(env);
117 try {
118 return new SimpleScalar(s.substring(index, index+1));
119 } catch (RuntimeException re) {
120 throw new TemplateException("", re, env);
121 }
122 }
123 catch(NonStringException e)
124 {
125 throw invalidTypeException(targetModel, target, env, "number, sequence, or string");
126 }
127 }
128
129
130 private TemplateModel dealWithStringKey(TemplateModel targetModel,
131 String key,
132 Environment env)
133 throws TemplateException
134 {
135 if(targetModel instanceof TemplateHashModel) {
136 return((TemplateHashModel) targetModel).get(key);
137 }
138 throw invalidTypeException(targetModel, target, env, "hash");
139 }
140
141 private TemplateModel dealWithRangeKey(TemplateModel targetModel,
142 Range range,
143 Environment env)
144 throws TemplateException
145 {
146 int start = EvaluationUtil.getNumber(range.left, env).intValue();
147 int end = 0;
148 boolean hasRhs = range.hasRhs();
149 if (hasRhs) {
150 end = EvaluationUtil.getNumber(range.right, env).intValue();
151 }
152 if (targetModel instanceof TemplateSequenceModel) {
153 TemplateSequenceModel sequence = (TemplateSequenceModel) targetModel;
154 if (!hasRhs) end = sequence.size() -1;
155 if (start < 0) {
156 String msg = range.left.getStartLocation() + "\nNegative starting index for range, is " + range;
157 throw new TemplateException(msg, env);
158 }
159 if (end < 0) {
160 String msg = range.left.getStartLocation() + "\nNegative ending index for range, is " + range;
161 throw new TemplateException(msg, env);
162 }
163 if (start >= sequence.size()) {
164 String msg = range.left.getStartLocation()
165 + "\nLeft side index of range out of bounds, is " + start
166 + ", but the sequence has only " + sequence.size() + " element(s) "
167 + "(note that indices are 0 based, and ranges are inclusive).";
168 throw new TemplateException(msg, env);
169 }
170 if (end >= sequence.size()) {
171 String msg = range.right.getStartLocation()
172 + "\nRight side index of range out of bounds, is " + end
173 + ", but the sequence has only " + sequence.size() + " element(s)."
174 + "(note that indices are 0 based, and ranges are inclusive).";
175 throw new TemplateException(msg, env);
176 }
177 ArrayList list = new ArrayList(1+Math.abs(start-end));
178 if (start>end) {
179 for (int i = start; i>=end; i--) {
180 list.add(sequence.get(i));
181 }
182 }
183 else {
184 for (int i = start; i<=end; i++) {
185 list.add(sequence.get(i));
186 }
187 }
188 return new SimpleSequence(list);
189 }
190
191 try
192 {
193 String s = target.getStringValue(env);
194 if (!hasRhs) end = s.length() -1;
195 if (start < 0) {
196 String msg = range.left.getStartLocation() + "\nNegative starting index for range " + range + " : " + start;
197 throw new TemplateException(msg, env);
198 }
199 if (end < 0) {
200 String msg = range.left.getStartLocation() + "\nNegative ending index for range " + range + " : " + end;
201 throw new TemplateException(msg, env);
202 }
203 if (start > s.length()) {
204 String msg = range.left.getStartLocation()
205 + "\nLeft side of range out of bounds, is: " + start
206 + "\nbut string " + targetModel + " has " + s.length() + " elements.";
207 throw new TemplateException(msg, env);
208 }
209 if (end > s.length()) {
210 String msg = range.right.getStartLocation()
211 + "\nRight side of range out of bounds, is: " + end
212 + "\nbut string " + targetModel + " is only " + s.length() + " characters.";
213 throw new TemplateException(msg, env);
214 }
215 try {
216 return new SimpleScalar(s.substring(start, end+1));
217 } catch (RuntimeException re) {
218 String msg = "Error " + getStartLocation();
219 throw new TemplateException(msg, re, env);
220 }
221 }
222 catch(NonStringException e)
223 {
224 throw invalidTypeException(target.getAsTemplateModel(env), target, env, "number, scalar, or sequence");
225 }
226 }
227
228 public String getCanonicalForm() {
229 return target.getCanonicalForm()
230 + "["
231 + nameExpression.getCanonicalForm()
232 + "]";
233 }
234
235 boolean isLiteral() {
236 return constantValue != null || (target.isLiteral() && nameExpression.isLiteral());
237 }
238
239 Expression _deepClone(String name, Expression subst) {
240 return new DynamicKeyName(target.deepClone(name, subst), nameExpression.deepClone(name, subst));
241 }
242 }