1 package org.apache.velocity.util.introspection;
2
3 /*
4 * Copyright 2006 The Apache Software Foundation.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License")
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import java.lang.reflect.Array;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.util.List;
23
24 /**
25 * Implementation of VelMethod to provide introspective "methods" for
26 * arrays that match those that would work on a fixed-size {@link List}.
27 * Currently only size(), isEmpty(), get(int), and set(int,Object) are
28 * supported. Later, support may be added for other read-only methods
29 * such as contains(Object) or subList(int,int). Patches are welcome! :)
30 *
31 * @author Nathan Bubna
32 * @version $Id: VelArrayMethod.java 440740 2006-09-06 15:37:44Z nbubna $
33 */
34 public class VelArrayMethod implements VelMethod
35 {
36 public static String SIZE = "size";
37 public static String IS_EMPTY = "isEmpty";
38 public static String GET = "get";
39 public static String SET = "set";
40
41 public static boolean supports(String methodName, Object[] params)
42 {
43 // quickest way to narrow things down is to switch
44 // on the number of parameters
45 switch (params.length)
46 {
47 case 0:
48 // then they must be calling one of these
49 return SIZE.equals(methodName) || IS_EMPTY.equals(methodName);
50 case 1:
51 // must be get() with a numeric param
52 return GET.equals(methodName) && isNumeric(params[0]);
53 case 2:
54 // must be set() with a numeric first param
55 return SET.equals(methodName) && isNumeric(params[0]);
56 default:
57 // it's not a supported method
58 return false;
59 }
60 }
61
62 protected static boolean isNumeric(Object param)
63 {
64 if (param != null && Number.class.isAssignableFrom(param.getClass()))
65 {
66 return true;
67 }
68 //TODO? do we need to check for primitive number types?
69 return false;
70 }
71
72
73 final Class arrayClass;
74 final String methodName;
75 final Object[] params;
76
77 public VelArrayMethod(Class arrayClass, String methodName, Object[] params)
78 {
79 this.methodName = methodName;
80 this.params = params;
81 this.arrayClass = arrayClass;
82 }
83
84 protected int toInt(Object param)
85 {
86 return ((Number)param).intValue();
87 }
88
89 public Object invoke(Object array, Object[] params) throws Exception
90 {
91 // quickest way to narrow things down is to switch
92 // on the number of parameters
93 switch (params.length)
94 {
95 // 0 params is either size() or isEmpty() (maybe iterator() someday)
96 case 0:
97 int length = Array.getLength(array);
98 if (SIZE.equals(methodName))
99 {
100 return new Integer(length);
101 }
102 if (IS_EMPTY.equals(methodName))
103 {
104 return Boolean.valueOf(length == 0);
105 }
106
107 // 1 param currently only could mean get() with a numeric param
108 // it could mean contains(), indexOf(), etc someday
109 case 1:
110 try
111 {
112 return Array.get(array, toInt(params[0]));
113 }
114 catch (RuntimeException re)
115 {
116 throw new InvocationTargetException(re);
117 }
118
119 // 2 params currently means set() with a numeric first param
120 // it could later mean subList(int,int) too
121 case 2:
122 try
123 {
124 int index = toInt(params[0]);
125 // get the old value to return it (like List does)
126 Object old = Array.get(array, index);
127 Array.set(array, index, params[1]);
128 return old;
129 }
130 catch (RuntimeException re)
131 {
132 throw new InvocationTargetException(re);
133 }
134
135 default:
136 // if supports() was checked before creating this instance
137 // then it should not be possible to get here
138 throw new UnsupportedOperationException('\'' + methodName +
139 "' with " + params.length +
140 " parameters is not a supported array method");
141 }
142 }
143
144 public boolean isCacheable()
145 {
146 return true;
147 }
148
149 public String getMethodName()
150 {
151 return methodName;
152 }
153
154 public Class getReturnType()
155 {
156 if (SIZE.equals(methodName))
157 {
158 return int.class;
159 }
160 if (GET.equals(methodName) || SET.equals(methodName))
161 {
162 // should this be Object.class instead?
163 return arrayClass.getComponentType();
164 }
165 if (IS_EMPTY.equals(methodName))
166 {
167 return boolean.class;
168 }
169 // not sure what else to do here
170 return Object.class;
171 }
172
173 }