1
24 package net.sf.jasperreports.engine.query;
25
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.concurrent.ConcurrentHashMap;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 import net.sf.jasperreports.engine.JRRuntimeException;
39 import net.sf.jasperreports.engine.JRValueParameter;
40 import net.sf.jasperreports.engine.util.Pair;
41
42
45 public class ParameterTypeSelectorClauseFunction implements JRClauseFunction
46 {
47
48 private static final Log log = LogFactory.getLog(ParameterTypeSelectorClauseFunction.class);
49 public static final String EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CLAUSE_IMPLEMENTATION_NOT_FOUND = "query.parameter.type.selector.clause.implementation.not.found";
50 public static final String EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CLAUSE_REQUIRED_TOKEN_NOT_FOUND = "query.parameter.type.selector.clause.required.token.not.found";
51
52 private static final String CONTEXT_KEY_FUNCTION_PER_TYPES_CACHE =
53 "net.sf.jasperreports.engine.query.ParameterTypeSelectorClauseFunction.cache";
54
55 private final int[] parameterPositions;
56
57 public ParameterTypeSelectorClauseFunction(int ... parameterPositions)
58 {
59 this.parameterPositions = parameterPositions;
60 }
61
62 @Override
63 public void apply(JRClauseTokens clauseTokens, JRQueryClauseContext queryContext)
64 {
65 List<Class<?>> parameterTypes = new ArrayList<Class<?>>(parameterPositions.length);
66 for (int position : parameterPositions)
67 {
68 Class<?> parameterType = determineParameterType(clauseTokens,
69 queryContext, position);
70 parameterTypes.add(parameterType);
71 }
72
73 JRClauseFunction function = getForParameterTypes(clauseTokens, queryContext, parameterTypes);
74 if (function == null)
75 {
76 throw
77 new JRRuntimeException(
78 EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CLAUSE_IMPLEMENTATION_NOT_FOUND,
79 new Object[]{clauseTokens.getClauseId(), parameterTypes});
80 }
81
82 function.apply(clauseTokens, queryContext);
83 }
84
85 protected Class<?> determineParameterType(JRClauseTokens clauseTokens,
86 JRQueryClauseContext queryContext, int parameterPosition)
87 {
88 String parameterName = clauseTokens.getToken(parameterPosition);
89 if (parameterName == null)
90 {
91 throw
92 new JRRuntimeException(
93 EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CLAUSE_IMPLEMENTATION_NOT_FOUND,
94 new Object[]{parameterPosition, clauseTokens.getClauseId()});
95 }
96
97
98
99 JRValueParameter parameter = queryContext.getValueParameter(parameterName);
100
101 Class<?> parameterType;
102 Object parameterValue = parameter.getValue();
103 if (parameterValue == null)
104 {
105
106 parameterType = parameter.getValueClass();
107 }
108 else
109 {
110
111 parameterType = parameterValue.getClass();
112 }
113
114 if (log.isDebugEnabled())
115 {
116 log.debug("query clause parameter " + parameterName
117 + " at position " + parameterPosition
118 + " has type " + parameterType.getName());
119 }
120
121 return parameterType;
122 }
123
124 protected JRClauseFunction getForParameterTypes(JRClauseTokens clauseTokens,
125 JRQueryClauseContext queryContext, List<Class<?>> parameterTypes)
126 {
127 Map<Object, JRClauseFunction> cache = getCache(queryContext);
128 Object typesKey = parameterTypesFunctionCacheKey(clauseTokens, queryContext, parameterTypes);
129 JRClauseFunction function = cache.get(typesKey);
130 if (function == null)
131 {
132 function = selectForParameterTypes(clauseTokens, queryContext, parameterTypes);
133 cache.put(typesKey, function);
134 }
135 else
136 {
137 if (log.isDebugEnabled())
138 {
139 log.debug("found cached function " + function
140 + " for clause " + clauseTokens.getClauseId() + " with types " + parameterTypes);
141 }
142 }
143 return function;
144 }
145
146 protected Map<Object, JRClauseFunction> getCache(
147 JRQueryClauseContext queryContext)
148 {
149 @SuppressWarnings("unchecked")
150 Map<Object, JRClauseFunction> cache = (Map<Object, JRClauseFunction>) queryContext.getJasperReportsContext().getOwnValue(
151 CONTEXT_KEY_FUNCTION_PER_TYPES_CACHE);
152 if (cache == null)
153 {
154
155 cache = new ConcurrentHashMap<Object, JRClauseFunction>();
156
157
158 queryContext.getJasperReportsContext().setValue(CONTEXT_KEY_FUNCTION_PER_TYPES_CACHE, cache);
159 }
160 return cache;
161 }
162
163 protected Object parameterTypesFunctionCacheKey(JRClauseTokens clauseTokens, JRQueryClauseContext queryContext,
164 List<Class<?>> parameterTypes)
165 {
166 Object typesKey;
167 int size = parameterTypes.size();
168 if (size == 1)
169 {
170
171 typesKey = parameterTypes.get(0);
172 }
173 else if (size == 2)
174 {
175
176 typesKey = new Pair<Class<?>, Class<?>>(parameterTypes.get(0), parameterTypes.get(1));
177 }
178 else
179 {
180
181 typesKey = parameterTypes;
182 }
183
184 Pair<String, String> clauseKey = new Pair<String, String>(
185 queryContext.getCanonicalQueryLanguage(), clauseTokens.getClauseId());
186 return new Pair<Pair<String, String>, Object>(clauseKey, typesKey);
187 }
188
189 protected JRClauseFunction selectForParameterTypes(JRClauseTokens clauseTokens,
190 JRQueryClauseContext queryContext, List<Class<?>> parameterTypes)
191 {
192 String queryLanguage = queryContext.getCanonicalQueryLanguage();
193 String clauseId = clauseTokens.getClauseId();
194
195 if (log.isDebugEnabled())
196 {
197 log.debug("selecting clause function " + clauseId + " for language " + queryLanguage
198 + " and parameter types " + parameterTypes);
199 }
200
201
202 List<ParameterTypesClauseFunctionBundle> functionsBundles = queryContext.getJasperReportsContext().getExtensions(
203 ParameterTypesClauseFunctionBundle.class);
204 List<Pair<List<Class<?>>, JRClauseFunction>> candidateFunctions = new ArrayList<Pair<List<Class<?>>,JRClauseFunction>>();
205 for (ParameterTypesClauseFunctionBundle functionsBundle : functionsBundles)
206 {
207 Collection<? extends ParameterTypesClauseFunction> functions = functionsBundle.getTypeFunctions(queryLanguage, clauseId);
208 if (functions != null)
209 {
210
211 for (ParameterTypesClauseFunction typesFunction : functions)
212 {
213 List<Class<?>> supportedTypes = findSupportedTypes(typesFunction, parameterTypes);
214 if (supportedTypes != null)
215 {
216 JRClauseFunction function = typesFunction.getFunction();
217 if (log.isDebugEnabled())
218 {
219 log.debug("found candidate function " + function
220 + " for types " + supportedTypes);
221 }
222
223 Pair<List<Class<?>>, JRClauseFunction> candidate =
224 new Pair<List<Class<?>>, JRClauseFunction>(supportedTypes, function);
225 candidateFunctions.add(candidate);
226 }
227 }
228 }
229 }
230
231 return selectFromCandidates(candidateFunctions);
232 }
233
234 protected JRClauseFunction selectFromCandidates(List<Pair<List<Class<?>>, JRClauseFunction>> candidateFunctions)
235 {
236 if (candidateFunctions.isEmpty())
237 {
238 return null;
239 }
240
241 if (candidateFunctions.size() == 1)
242 {
243
244 return candidateFunctions.get(0).second();
245 }
246
247
248 Collections.sort(candidateFunctions, TypesCandidateComparator.INSTANCE);
249
250 JRClauseFunction function = candidateFunctions.get(0).second();
251 if (log.isDebugEnabled())
252 {
253 log.debug("selected function " + function);
254 }
255 return function;
256 }
257
258 protected List<Class<?>> findSupportedTypes(
259 ParameterTypesClauseFunction typesFunction,
260 List<Class<?>> parameterTypes)
261 {
262 Collection<Class<?>> functionTypes = typesFunction.getSupportedTypes();
263 List<Class<?>> supportedTypes = new ArrayList<Class<?>>(parameterTypes.size());
264 for (Class<?> paramType : parameterTypes)
265 {
266 Class<?> supportedType = findSupportedType(functionTypes, paramType);
267 if (supportedType == null)
268 {
269 break;
270 }
271 else
272 {
273 supportedTypes.add(supportedType);
274 }
275 }
276
277 if (supportedTypes.size() == parameterTypes.size())
278 {
279
280 return supportedTypes;
281 }
282 return null;
283 }
284
285 protected Class<?> findSupportedType(Collection<Class<?>> supportedTypes, Class<?> parameterType)
286 {
287 for (Class<?> supportedType : supportedTypes)
288 {
289 if (supportedType.isAssignableFrom(parameterType))
290 {
291 return supportedType;
292 }
293 }
294 return null;
295 }
296 }
297
298 final class TypesCandidateComparator implements Comparator<Pair<List<Class<?>>, JRClauseFunction>>
299 {
300 public static final String EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CANDIDATE_TYPE_SIZE_MISMATCH = "query.parameter.type.selector.candidate.type.size.mismatch";
301
302 protected static final TypesCandidateComparator INSTANCE = new TypesCandidateComparator();
303
304 private TypesCandidateComparator()
305 {
306 }
307
308 @Override
309 public int compare(Pair<List<Class<?>>, JRClauseFunction> o1,
310 Pair<List<Class<?>>, JRClauseFunction> o2)
311 {
312 List<Class<?>> types1 = o1.first();
313 List<Class<?>> types2 = o2.first();
314
315
316 if (types1.size() != types2.size())
317 {
318 throw
319 new JRRuntimeException(
320 EXCEPTION_MESSAGE_KEY_QUERY_PARAMETER_TYPE_SELECTOR_CANDIDATE_TYPE_SIZE_MISMATCH,
321 new Object[]{types1.size(), types2.size()});
322 }
323
324
325 int order = 0;
326 for (Iterator<Class<?>> it1 = types1.iterator(), it2 = types2.iterator(); it1.hasNext() && it2.hasNext(); )
327 {
328 Class<?> type1 = it1.next();
329 Class<?> type2 = it2.next();
330 int typesOrder = compareTypes(type1, type2);
331 if (typesOrder != 0)
332 {
333 order = typesOrder;
334 break;
335 }
336 }
337
338 return order;
339 }
340
341 protected int compareTypes(Class<?> type1, Class<?> type2)
342 {
343 if (type1.equals(type2))
344 {
345 return 0;
346 }
347
348
349 if (type1.isAssignableFrom(type2))
350 {
351 return 1;
352 }
353
354 if (type2.isAssignableFrom(type1))
355 {
356 return -1;
357 }
358
359
360 return type1.getName().compareTo(type2.getName());
361 }
362
363 }