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.Method;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.peaseplate.TemplateRuntimeException;
28  import org.peaseplate.internal.BuildContext;
29  import org.peaseplate.internal.util.ReflectionException;
30  import org.peaseplate.internal.util.ReflectionUtils;
31  import org.peaseplate.locator.TemplateLocator;
32  
33  /**
34   * This command does the whole magic. It invokes the method
35   * that matches the identifier and the parameters. Since the 
36   * language is not strictly type, this may be complicated
37   * some times.
38   * 
39   * Any method that has a name similar to the identifier,
40   * or the identifier prefixed with "get", "is" or "set"
41   * (the case is ignored) will be taken into consideration.
42   * 
43   * Next it checks the number (not the type) of the parameters.
44   * 
45   * Hopefully this results in just one method. If there are still 
46   * multiple methods with name and the same parameter count,
47   * it searches for the method that most exactly fits the types
48   * of the parameters (this may vary each time the command is
49   * called, because the language is not strictly typed).
50   * 
51   * To get this whole stuff acceptable fast, it stores all possible
52   * methods in a list the first time the command is invoked.
53   * 
54   * Due to the fact, that the language is not strictly typed, it
55   * even is possible that the class may change on which
56   * this command is invoked. Thus the list with the commands is
57   * stored in a hash map with the class as key.
58   */
59  public class QueryCommand extends AbstractObjectCallCommand {
60  
61  	private final ICommand identifierCommand;
62  	
63  	public QueryCommand(TemplateLocator locator, int line, int column, ICommand command, ICommand identifierCommand, ICommand[] parameterCommands) {
64  		super(locator, line, column, command, parameterCommands);
65  		
66  		this.identifierCommand = identifierCommand;
67  	}
68  	
69  	/**
70  	 * @see org.peaseplate.internal.lang.command.ICommand#call(BuildContext)
71  	 */
72  	public Object call(BuildContext context) throws TemplateRuntimeException {
73  		Object workingObject = callCommand(context);
74  
75  		if (workingObject == null)
76  			return null;
77  		
78  		return callObject(context, workingObject, identifierCommand.call(context));
79  	}
80  
81  	/**
82  	 * @see org.peaseplate.internal.lang.command.AbstractObjectCallCommand#callObject(org.peaseplate.internal.BuildContext, java.lang.Object, java.lang.Object)
83  	 */
84  	@Override
85      protected Object callObject(BuildContext context, Object onObject, Object identifier) throws TemplateRuntimeException {
86  		if (onObject == null)
87  			return null;
88  		
89  		if (onObject instanceof Map)
90  			return callMap(context, (Map<?, ?>)onObject, identifier);
91  		
92  		else if ((onObject instanceof List) && (identifier instanceof Number))
93  			return callList((List<?>)onObject, ((Number)identifier).intValue());
94  		
95  		else if ((onObject.getClass().isArray()) && (identifier instanceof Number))
96  			return callArray((Object[])onObject, ((Number)identifier).intValue());
97  		
98  		return callFieldOrMethod(context, onObject, String.valueOf(identifier));
99  	}
100 
101 	/**
102 	 * @see org.peaseplate.internal.lang.command.AbstractNativeCallCommand#getField(java.lang.Class, java.lang.String)
103 	 */
104 	@Override
105     protected Field getField(Class<?> clazz, String identifier) throws TemplateRuntimeException {
106 	    return findField(clazz, identifier);
107     }
108 
109 	/**
110 	 * @see org.peaseplate.internal.lang.command.AbstractObjectCallCommand#getMethod(java.lang.Class, java.lang.String, int)
111 	 */
112 	@Override
113     protected Method getMethod(Class<?> clazz, String identifier, int numberOfParameters) throws TemplateRuntimeException {
114 		try {
115 			return ReflectionUtils.findMethod(clazz, identifier, numberOfParameters);
116 		}
117 		catch (ReflectionException e) {
118 			throw new TemplateRuntimeException(
119 				getLocator(), getLine(), getColumn(),
120 				"Could not find method \"" + identifier + "\" in " + clazz, e
121 			);
122 		}
123     }
124 
125 	/**
126 	 * @see java.lang.Object#toString()
127 	 */
128 	@Override
129 	public String toString() {
130 		StringBuilder result = new StringBuilder();
131 		
132 		if (getCommand() != null)
133 			result.append(getCommand());
134 
135 		result.append("[").append(identifierCommand).append("]");
136 		result.append(super.toString());
137 		
138 		return result.toString();
139 	}
140 	
141 }