View Javadoc

1   /*
2    * This file is part of Pease Plate Template Engine.
3    * 
4    * Pease Plate Template Engine is free software: you can redistribute
5    * it and/or modify it under the terms of the GNU Lesser General 
6    * Public License as published by the Free Software Foundation, 
7    * either version 3 of the License, or any later version.
8    * 
9    * Pease Plate Template Engine is distributed in the hope that it 
10   * will be useful, but WITHOUT ANY WARRANTY; without even the implied
11   * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12   * See the GNU Lesser General Public License for more details.
13   * 
14   * You should have received a copy of the GNU Lesser General Public 
15   * License along with Pease Plate Template Engine. If not, see 
16   * <http://www.gnu.org/licenses/>.
17   * 
18   * Copyright (c) 2008 Manfred HANTSCHEL
19   */
20  package org.peaseplate.internal.lang.command;
21  
22  import java.lang.reflect.Field;
23  import java.lang.reflect.InvocationTargetException;
24  import java.lang.reflect.Method;
25  import java.util.List;
26  import java.util.Locale;
27  import java.util.Map;
28  
29  import org.peaseplate.TemplateRuntimeException;
30  import org.peaseplate.internal.BuildContext;
31  import org.peaseplate.internal.util.ReflectionException;
32  import org.peaseplate.internal.util.ReflectionUtils;
33  import org.peaseplate.locator.TemplateLocator;
34  
35  public abstract class AbstractObjectCallCommand extends AbstractNativeCallCommand {
36  
37  	public AbstractObjectCallCommand(TemplateLocator locator, int line, int column, ICommand command, ICommand... parameterCommands) {
38  	    super(locator, line, column, command, parameterCommands);
39      }
40  	
41  	/**
42  	 * Distinguishes between maps, lists and other objects. 
43  	 * 
44  	 * If object is a map, it resolves the return value from the map.
45  	 * 
46  	 * If the object is a list and the identifier is a number, it resolves 
47  	 * the return value from the list.
48  	 * 
49  	 * If the object is an array and the identifier is a number, it resolves
50  	 * the return value from the array.
51  	 *  
52  	 * In any other case, it invokes the method with the specified identifier
53  	 * and the parameters on the object.
54  	 * 
55  	 * It depends on the call in which order the options are used.
56  	 * 
57  	 * @param onObject the object
58  	 * @param identifier the name of the map entry, the index of the list, or the name of the method.
59  	 * @param parameters the parameters
60  	 * @return the result
61  	 */
62  	protected abstract Object callObject(BuildContext context, Object onObject, Object identifier) throws TemplateRuntimeException;
63  	
64  	protected Object callField(Object onObject, Field field) throws TemplateRuntimeException {
65  		try {
66  	        return field.get(onObject);
67          }
68          catch (IllegalArgumentException e) {
69  			throw new TemplateRuntimeException(
70  				getLocator(), getLine(), getColumn(),
71  				"Illegal argument exception when calling field \"" + field + "\"", e
72  			);
73          }
74          catch (IllegalAccessException e) {
75  			throw new TemplateRuntimeException(
76  				getLocator(), getLine(), getColumn(),
77  				"Cannot access field \"" + field + "\"", e
78  			);
79          }
80  	}
81  	
82  	protected Object callMap(BuildContext context, Map<?, ?> onMap, Object identifier) throws TemplateRuntimeException {
83  		if (hasParameters())
84  			return callMethod(context, onMap, String.valueOf(identifier));
85  
86  		return onMap.get(identifier);
87  	}
88  	
89  	protected Object callList(List<?> onList, int index) throws TemplateRuntimeException {
90  		if (hasParameters())
91  			throw new TemplateRuntimeException(
92  				getLocator(), getLine(), getColumn(), "Calls on a List must not have parameters"
93  			);
94  		
95  		if ((index < 0) || (index >= onList.size()))
96  			return null;
97  		
98  		return onList.get(index);
99  	}
100 	
101 	protected Object callArray(Object[] onArray, int index) throws TemplateRuntimeException {
102 		if (hasParameters())
103 			throw new TemplateRuntimeException(
104 				getLocator(), getLine(), getColumn(), "Calls on an Array must not have parameters"
105 			);
106 		
107 		if ((index < 0) || (index >= onArray.length))
108 			return null;
109 		
110 		return onArray[index];
111 	}
112 
113 	protected Object callFieldOrMethod(BuildContext context, Object onObject, String identifier) throws TemplateRuntimeException {
114 		identifier = identifier.toLowerCase(Locale.getDefault());
115 		
116 		if (!hasParameters()) {
117 			Field field = getField(onObject.getClass(), identifier);
118 			
119 			if (field != null)
120 				return callField(onObject, field);
121 		}
122 		
123 		return callMethod(context, onObject, identifier);
124 	}
125 	
126 	protected Object callMethod(BuildContext context, Object onObject, String identifier) throws TemplateRuntimeException {
127 		Method method = getMethod(onObject.getClass(), identifier.toLowerCase(Locale.getDefault()), getNumberOfParameters());
128 		
129 		if (method == null)
130 			throw new TemplateRuntimeException(
131 				getLocator(), getLine(), getColumn(), 
132 				"No method matches " + ReflectionUtils.formatMethodName(onObject.getClass(), identifier, getNumberOfParameters())
133 			);
134 		
135 		return callMethod(context, onObject, method);
136 	}
137 
138 	protected Object callMethod(BuildContext context, Object onObject, Method method) throws TemplateRuntimeException {
139 		try {
140 			Object[] parameters = ReflectionUtils.callParameterCommands(context, getParameterCommands(), method);
141 
142 			return method.invoke(onObject, parameters);
143 		}
144 		catch (ReflectionException e) {
145 			throw new TemplateRuntimeException(
146 				getLocator(), getLine(), getColumn(),
147 				"Could not call " + method, e
148 			);
149 		}
150 		
151 		catch (IllegalAccessException e) {
152 			throw new TemplateRuntimeException(
153 				getLocator(), getLine(), getColumn(),
154 				"Illegal access on method " + method, e
155 			);
156 		}
157 		catch (IllegalArgumentException e) {
158 			throw new TemplateRuntimeException(
159 				getLocator(), getLine(), getColumn(),
160 				"Invalid argument when calling method " + method, e
161 			);
162 		}
163 		catch (InvocationTargetException e) {
164 			throw new TemplateRuntimeException(
165 				getLocator(), getLine(), getColumn(),
166 				"Call on " + method + " threw exception", e
167 			);
168 		}
169 	}
170 	
171 	protected abstract Field getField(Class<?> clazz, String identifier) throws TemplateRuntimeException;
172 	
173 	protected Field findField(Class<?> clazz, String identifier) throws TemplateRuntimeException {
174 		identifier = identifier.toLowerCase(Locale.getDefault());
175 		
176 		return scanFields(clazz, identifier);
177 	}
178 	
179 	protected Field scanFields(Class<?> clazz, String identifier) throws TemplateRuntimeException {
180 		Field[] fields = clazz.getFields();
181 		
182 		for (int i=fields.length-1; i>=0; i-=1)
183 			if (identifier.equalsIgnoreCase(fields[i].getName()))
184 				return fields[i];
185 
186 		return null;
187 	}
188 		
189 	protected abstract Method getMethod(Class<?> clazz, String identifier, int numberOfParameters) throws TemplateRuntimeException;
190 	
191 }