/beansbinding-1.2.1/src/org/jdesktop/el/CompositeELResolver.java

# · Java · 475 lines · 152 code · 20 blank · 303 comment · 33 complexity · 7b6224a7de33c5f703f356c8e6a3d3f5 MD5 · raw file

  1. /*
  2. * Copyright (C) 2007 Sun Microsystems, Inc. All rights reserved. Use is
  3. * subject to license terms.
  4. */
  5. package org.jdesktop.el;
  6. import java.util.ArrayList;
  7. import java.util.Iterator;
  8. import java.beans.FeatureDescriptor;
  9. /**
  10. * Maintains an ordered composite list of child <code>ELResolver</code>s.
  11. *
  12. * <p>Though only a single <code>ELResolver</code> is associated with an
  13. * <code>ELContext</code>, there are usually multiple resolvers considered
  14. * for any given variable or property resolution. <code>ELResolver</code>s
  15. * are combined together using a <code>CompositeELResolver</code>, to define
  16. * rich semantics for evaluating an expression.</p>
  17. *
  18. * <p>For the {@link #getValue}, {@link #getType}, {@link #setValue} and
  19. * {@link #isReadOnly} methods, an <code>ELResolver</code> is not
  20. * responsible for resolving all possible (base, property) pairs. In fact,
  21. * most resolvers will only handle a <code>base</code> of a single type.
  22. * To indicate that a resolver has successfully resolved a particular
  23. * (base, property) pair, it must set the <code>propertyResolved</code>
  24. * property of the <code>ELContext</code> to <code>true</code>. If it could
  25. * not handle the given pair, it must leave this property alone. The caller
  26. * must ignore the return value of the method if <code>propertyResolved</code>
  27. * is <code>false</code>.</p>
  28. *
  29. * <p>The <code>CompositeELResolver</code> initializes the
  30. * <code>ELContext.propertyResolved</code> flag to <code>false</code>, and uses
  31. * it as a stop condition for iterating through its component resolvers.</p>
  32. *
  33. * <p>The <code>ELContext.propertyResolved</code> flag is not used for the
  34. * design-time methods {@link #getFeatureDescriptors} and
  35. * {@link #getCommonPropertyType}. Instead, results are collected and
  36. * combined from all child <code>ELResolver</code>s for these methods.</p>
  37. *
  38. * @see ELContext
  39. * @see ELResolver
  40. * @since JSP 2.1
  41. */
  42. public class CompositeELResolver extends ELResolver {
  43. /**
  44. * Adds the given resolver to the list of component resolvers.
  45. *
  46. * <p>Resolvers are consulted in the order in which they are added.</p>
  47. *
  48. * @param elResolver The component resolver to add.
  49. * @throws NullPointerException If the provided resolver is
  50. * <code>null</code>.
  51. */
  52. public void add(ELResolver elResolver) {
  53. if (elResolver == null) {
  54. throw new NullPointerException();
  55. }
  56. elResolvers.add(elResolver);
  57. }
  58. /**
  59. * Attempts to resolve the given <code>property</code> object on the given
  60. * <code>base</code> object by querying all component resolvers.
  61. *
  62. * <p>If this resolver handles the given (base, property) pair,
  63. * the <code>propertyResolved</code> property of the
  64. * <code>ELContext</code> object must be set to <code>true</code>
  65. * by the resolver, before returning. If this property is not
  66. * <code>true</code> after this method is called, the caller should ignore
  67. * the return value.</p>
  68. *
  69. * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  70. * the provided <code>ELContext</code>.</p>
  71. *
  72. * <p>Next, for each component resolver in this composite:
  73. * <ol>
  74. * <li>The <code>getValue()</code> method is called, passing in
  75. * the provided <code>context</code>, <code>base</code> and
  76. * <code>property</code>.</li>
  77. * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  78. * flag is <code>false</code> then iteration continues.</li>
  79. * <li>Otherwise, iteration stops and no more component resolvers are
  80. * considered. The value returned by <code>getValue()</code> is
  81. * returned by this method.</li>
  82. * </ol></p>
  83. *
  84. * <p>If none of the component resolvers were able to perform this
  85. * operation, the value <code>null</code> is returned and the
  86. * <code>propertyResolved</code> flag remains set to
  87. * <code>false</code></p>.
  88. *
  89. * <p>Any exception thrown by component resolvers during the iteration
  90. * is propagated to the caller of this method.</p>
  91. *
  92. * @param context The context of this evaluation.
  93. * @param base The base object whose property value is to be returned,
  94. * or <code>null</code> to resolve a top-level variable.
  95. * @param property The property or variable to be resolved.
  96. * @return If the <code>propertyResolved</code> property of
  97. * <code>ELContext</code> was set to <code>true</code>, then
  98. * the result of the variable or property resolution; otherwise
  99. * undefined.
  100. * @throws NullPointerException if context is <code>null</code>
  101. * @throws PropertyNotFoundException if the given (base, property) pair
  102. * is handled by this <code>ELResolver</code> but the specified
  103. * variable or property does not exist or is not readable.
  104. * @throws ELException if an exception was thrown while performing
  105. * the property or variable resolution. The thrown exception
  106. * must be included as the cause property of this exception, if
  107. * available.
  108. */
  109. public Object getValue(ELContext context,
  110. Object base,
  111. Object property) {
  112. context.setPropertyResolved(false);
  113. int i = 0, len = this.elResolvers.size();
  114. ELResolver elResolver;
  115. Object value;
  116. while (i < len) {
  117. elResolver = this.elResolvers.get(i);
  118. value = elResolver.getValue(context, base, property);
  119. if (context.isPropertyResolved()) {
  120. return value;
  121. }
  122. i++;
  123. }
  124. return ELContext.UNRESOLVABLE_RESULT;
  125. }
  126. /**
  127. * For a given <code>base</code> and <code>property</code>, attempts to
  128. * identify the most general type that is acceptable for an object to be
  129. * passed as the <code>value</code> parameter in a future call
  130. * to the {@link #setValue} method. The result is obtained by
  131. * querying all component resolvers.
  132. *
  133. * <p>If this resolver handles the given (base, property) pair,
  134. * the <code>propertyResolved</code> property of the
  135. * <code>ELContext</code> object must be set to <code>true</code>
  136. * by the resolver, before returning. If this property is not
  137. * <code>true</code> after this method is called, the caller should ignore
  138. * the return value.</p>
  139. *
  140. * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  141. * the provided <code>ELContext</code>.</p>
  142. *
  143. * <p>Next, for each component resolver in this composite:
  144. * <ol>
  145. * <li>The <code>getType()</code> method is called, passing in
  146. * the provided <code>context</code>, <code>base</code> and
  147. * <code>property</code>.</li>
  148. * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  149. * flag is <code>false</code> then iteration continues.</li>
  150. * <li>Otherwise, iteration stops and no more component resolvers are
  151. * considered. The value returned by <code>getType()</code> is
  152. * returned by this method.</li>
  153. * </ol></p>
  154. *
  155. * <p>If none of the component resolvers were able to perform this
  156. * operation, the value <code>null</code> is returned and the
  157. * <code>propertyResolved</code> flag remains set to
  158. * <code>false</code></p>.
  159. *
  160. * <p>Any exception thrown by component resolvers during the iteration
  161. * is propagated to the caller of this method.</p>
  162. *
  163. * @param context The context of this evaluation.
  164. * @param base The base object whose property value is to be analyzed,
  165. * or <code>null</code> to analyze a top-level variable.
  166. * @param property The property or variable to return the acceptable
  167. * type for.
  168. * @return If the <code>propertyResolved</code> property of
  169. * <code>ELContext</code> was set to <code>true</code>, then
  170. * the most general acceptable type; otherwise undefined.
  171. * @throws NullPointerException if context is <code>null</code>
  172. * @throws PropertyNotFoundException if the given (base, property) pair
  173. * is handled by this <code>ELResolver</code> but the specified
  174. * variable or property does not exist or is not readable.
  175. * @throws ELException if an exception was thrown while performing
  176. * the property or variable resolution. The thrown exception
  177. * must be included as the cause property of this exception, if
  178. * available.
  179. */
  180. public Class<?> getType(ELContext context,
  181. Object base,
  182. Object property) {
  183. context.setPropertyResolved(false);
  184. int i = 0, len = this.elResolvers.size();
  185. ELResolver elResolver;
  186. Class<?> type;
  187. while (i < len) {
  188. elResolver = this.elResolvers.get(i);
  189. type = elResolver.getType(context, base, property);
  190. if (context.isPropertyResolved()) {
  191. return type;
  192. }
  193. i++;
  194. }
  195. return null;
  196. }
  197. /**
  198. * Attempts to set the value of the given <code>property</code>
  199. * object on the given <code>base</code> object. All component
  200. * resolvers are asked to attempt to set the value.
  201. *
  202. * <p>If this resolver handles the given (base, property) pair,
  203. * the <code>propertyResolved</code> property of the
  204. * <code>ELContext</code> object must be set to <code>true</code>
  205. * by the resolver, before returning. If this property is not
  206. * <code>true</code> after this method is called, the caller can
  207. * safely assume no value has been set.</p>
  208. *
  209. * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  210. * the provided <code>ELContext</code>.</p>
  211. *
  212. * <p>Next, for each component resolver in this composite:
  213. * <ol>
  214. * <li>The <code>setValue()</code> method is called, passing in
  215. * the provided <code>context</code>, <code>base</code>,
  216. * <code>property</code> and <code>value</code>.</li>
  217. * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  218. * flag is <code>false</code> then iteration continues.</li>
  219. * <li>Otherwise, iteration stops and no more component resolvers are
  220. * considered.</li>
  221. * </ol></p>
  222. *
  223. * <p>If none of the component resolvers were able to perform this
  224. * operation, the <code>propertyResolved</code> flag remains set to
  225. * <code>false</code></p>.
  226. *
  227. * <p>Any exception thrown by component resolvers during the iteration
  228. * is propagated to the caller of this method.</p>
  229. *
  230. * @param context The context of this evaluation.
  231. * @param base The base object whose property value is to be set,
  232. * or <code>null</code> to set a top-level variable.
  233. * @param property The property or variable to be set.
  234. * @param val The value to set the property or variable to.
  235. * @throws NullPointerException if context is <code>null</code>
  236. * @throws PropertyNotFoundException if the given (base, property) pair
  237. * is handled by this <code>ELResolver</code> but the specified
  238. * variable or property does not exist.
  239. * @throws PropertyNotWritableException if the given (base, property)
  240. * pair is handled by this <code>ELResolver</code> but the specified
  241. * variable or property is not writable.
  242. * @throws ELException if an exception was thrown while attempting to
  243. * set the property or variable. The thrown exception
  244. * must be included as the cause property of this exception, if
  245. * available.
  246. */
  247. public void setValue(ELContext context,
  248. Object base,
  249. Object property,
  250. Object val) {
  251. context.setPropertyResolved(false);
  252. int i = 0, len = this.elResolvers.size();
  253. ELResolver elResolver;
  254. while (i < len) {
  255. elResolver = this.elResolvers.get(i);
  256. elResolver.setValue(context, base, property, val);
  257. if (context.isPropertyResolved()) {
  258. return;
  259. }
  260. i++;
  261. }
  262. }
  263. /**
  264. * For a given <code>base</code> and <code>property</code>, attempts to
  265. * determine whether a call to {@link #setValue} will always fail. The
  266. * result is obtained by querying all component resolvers.
  267. *
  268. * <p>If this resolver handles the given (base, property) pair,
  269. * the <code>propertyResolved</code> property of the
  270. * <code>ELContext</code> object must be set to <code>true</code>
  271. * by the resolver, before returning. If this property is not
  272. * <code>true</code> after this method is called, the caller should ignore
  273. * the return value.</p>
  274. *
  275. * <p>First, <code>propertyResolved</code> is set to <code>false</code> on
  276. * the provided <code>ELContext</code>.</p>
  277. *
  278. * <p>Next, for each component resolver in this composite:
  279. * <ol>
  280. * <li>The <code>isReadOnly()</code> method is called, passing in
  281. * the provided <code>context</code>, <code>base</code> and
  282. * <code>property</code>.</li>
  283. * <li>If the <code>ELContext</code>'s <code>propertyResolved</code>
  284. * flag is <code>false</code> then iteration continues.</li>
  285. * <li>Otherwise, iteration stops and no more component resolvers are
  286. * considered. The value returned by <code>isReadOnly()</code> is
  287. * returned by this method.</li>
  288. * </ol></p>
  289. *
  290. * <p>If none of the component resolvers were able to perform this
  291. * operation, the value <code>false</code> is returned and the
  292. * <code>propertyResolved</code> flag remains set to
  293. * <code>false</code></p>.
  294. *
  295. * <p>Any exception thrown by component resolvers during the iteration
  296. * is propagated to the caller of this method.</p>
  297. *
  298. * @param context The context of this evaluation.
  299. * @param base The base object whose property value is to be analyzed,
  300. * or <code>null</code> to analyze a top-level variable.
  301. * @param property The property or variable to return the read-only status
  302. * for.
  303. * @return If the <code>propertyResolved</code> property of
  304. * <code>ELContext</code> was set to <code>true</code>, then
  305. * <code>true</code> if the property is read-only or
  306. * <code>false</code> if not; otherwise undefined.
  307. * @throws NullPointerException if context is <code>null</code>
  308. * @throws PropertyNotFoundException if the given (base, property) pair
  309. * is handled by this <code>ELResolver</code> but the specified
  310. * variable or property does not exist.
  311. * @throws ELException if an exception was thrown while performing
  312. * the property or variable resolution. The thrown exception
  313. * must be included as the cause property of this exception, if
  314. * available.
  315. */
  316. public boolean isReadOnly(ELContext context,
  317. Object base,
  318. Object property) {
  319. context.setPropertyResolved(false);
  320. int i = 0, len = this.elResolvers.size();
  321. ELResolver elResolver;
  322. boolean readOnly;
  323. while (i < len) {
  324. elResolver = this.elResolvers.get(i);
  325. readOnly = elResolver.isReadOnly(context, base, property);
  326. if (context.isPropertyResolved()) {
  327. return readOnly;
  328. }
  329. i++;
  330. }
  331. return false; // Does not matter
  332. }
  333. /**
  334. * Returns information about the set of variables or properties that
  335. * can be resolved for the given <code>base</code> object. One use for
  336. * this method is to assist tools in auto-completion. The results are
  337. * collected from all component resolvers.
  338. *
  339. * <p>The <code>propertyResolved</code> property of the
  340. * <code>ELContext</code> is not relevant to this method.
  341. * The results of all <code>ELResolver</code>s are concatenated.</p>
  342. *
  343. * <p>The <code>Iterator</code> returned is an iterator over the
  344. * collection of <code>FeatureDescriptor</code> objects returned by
  345. * the iterators returned by each component resolver's
  346. * <code>getFeatureDescriptors</code> method. If <code>null</code> is
  347. * returned by a resolver, it is skipped.</p>
  348. *
  349. * @param context The context of this evaluation.
  350. * @param base The base object whose set of valid properties is to
  351. * be enumerated, or <code>null</code> to enumerate the set of
  352. * top-level variables that this resolver can evaluate.
  353. * @return An <code>Iterator</code> containing zero or more (possibly
  354. * infinitely more) <code>FeatureDescriptor</code> objects, or
  355. * <code>null</code> if this resolver does not handle the given
  356. * <code>base</code> object or that the results are too complex to
  357. * represent with this method
  358. */
  359. public Iterator<FeatureDescriptor> getFeatureDescriptors(
  360. ELContext context,
  361. Object base) {
  362. return new CompositeIterator(elResolvers.iterator(), context, base);
  363. }
  364. /**
  365. * Returns the most general type that this resolver accepts for the
  366. * <code>property</code> argument, given a <code>base</code> object.
  367. * One use for this method is to assist tools in auto-completion. The
  368. * result is obtained by querying all component resolvers.
  369. *
  370. * <p>The <code>Class</code> returned is the most specific class that is
  371. * a common superclass of all the classes returned by each component
  372. * resolver's <code>getCommonPropertyType</code> method. If
  373. * <code>null</code> is returned by a resolver, it is skipped.</p>
  374. *
  375. * @param context The context of this evaluation.
  376. * @param base The base object to return the most general property
  377. * type for, or <code>null</code> to enumerate the set of
  378. * top-level variables that this resolver can evaluate.
  379. * @return <code>null</code> if this <code>ELResolver</code> does not
  380. * know how to handle the given <code>base</code> object; otherwise
  381. * <code>Object.class</code> if any type of <code>property</code>
  382. * is accepted; otherwise the most general <code>property</code>
  383. * type accepted for the given <code>base</code>.
  384. */
  385. public Class<?> getCommonPropertyType(ELContext context,
  386. Object base) {
  387. Class<?> commonPropertyType = null;
  388. Iterator<ELResolver> iter = elResolvers.iterator();
  389. while (iter.hasNext()) {
  390. ELResolver elResolver = iter.next();
  391. Class<?> type = elResolver.getCommonPropertyType(context, base);
  392. if (type == null) {
  393. // skip this EL Resolver
  394. continue;
  395. } else if (commonPropertyType == null) {
  396. commonPropertyType = type;
  397. } else if (commonPropertyType.isAssignableFrom(type)) {
  398. continue;
  399. } else if (type.isAssignableFrom(commonPropertyType)) {
  400. commonPropertyType = type;
  401. } else {
  402. // Don't have a commonPropertyType
  403. return null;
  404. }
  405. }
  406. return commonPropertyType;
  407. }
  408. private final ArrayList<ELResolver> elResolvers =
  409. new ArrayList<ELResolver>();
  410. private static class CompositeIterator
  411. implements Iterator<FeatureDescriptor> {
  412. Iterator<ELResolver> compositeIter;
  413. Iterator<FeatureDescriptor> propertyIter;
  414. ELContext context;
  415. Object base;
  416. CompositeIterator(Iterator<ELResolver> iter,
  417. ELContext context,
  418. Object base) {
  419. compositeIter = iter;
  420. this.context = context;
  421. this.base = base;
  422. }
  423. public boolean hasNext() {
  424. if (propertyIter == null || !propertyIter.hasNext()) {
  425. while (compositeIter.hasNext()) {
  426. ELResolver elResolver = compositeIter.next();
  427. propertyIter = elResolver.getFeatureDescriptors(
  428. context, base);
  429. if (propertyIter != null) {
  430. return propertyIter.hasNext();
  431. }
  432. }
  433. return false;
  434. }
  435. return propertyIter.hasNext();
  436. }
  437. public FeatureDescriptor next() {
  438. if (propertyIter == null || !propertyIter.hasNext()) {
  439. while (compositeIter.hasNext()) {
  440. ELResolver elResolver = compositeIter.next();
  441. propertyIter = elResolver.getFeatureDescriptors(
  442. context, base);
  443. if (propertyIter != null) {
  444. return propertyIter.next();
  445. }
  446. }
  447. return null;
  448. }
  449. return propertyIter.next();
  450. }
  451. public void remove() {
  452. throw new UnsupportedOperationException();
  453. }
  454. }
  455. }