1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package groovy.util;
47
48 import groovy.lang.ParameterArray;
49 import groovy.lang.Closure;
50 import groovy.lang.GroovyObjectSupport;
51 import groovy.lang.GroovyRuntimeException;
52 import groovy.lang.MetaExpandoProperty;
53
54 import java.util.HashMap;
55 import java.util.Map;
56 import java.util.Map.Entry;
57 import java.util.List;
58 import java.util.ArrayList;
59 import java.util.Iterator;
60
61
62 /***
63 * Represents a dynamically expandable bean.
64 *
65 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
66 * @author Hein Meling
67 * @author Pilho Kim
68 * @version $Revision: 1.6 $
69 */
70 public class Expando extends GroovyObjectSupport {
71
72 private Map expandoProperties;
73
74 public Expando() {
75 }
76
77 public Expando(Map expandoProperties) {
78 this.expandoProperties = expandoProperties;
79 }
80
81 /***
82 * @return the dynamically expanded properties
83 */
84 public Map getExpandoProperties() {
85 if (expandoProperties == null) {
86 expandoProperties = createMap();
87 }
88 return expandoProperties;
89 }
90
91 public List getProperties() {
92
93 List ret = new ArrayList();
94 Iterator itr = getExpandoProperties().entrySet().iterator();
95 while(itr.hasNext()) {
96 Entry entry = (Entry) itr.next();
97 ret.add(new MetaExpandoProperty(entry));
98 }
99
100 return ret;
101 }
102
103 public Object getProperty(String property) {
104 try {
105 return super.getProperty(property);
106 }
107 catch (GroovyRuntimeException e) {
108 return getExpandoProperties().get(property);
109 }
110 }
111
112 public void setProperty(String property, Object newValue) {
113 try {
114 super.setProperty(property, newValue);
115 }
116 catch (GroovyRuntimeException e) {
117 getExpandoProperties().put(property, newValue);
118 }
119 }
120
121 public Object invokeMethod(String name, Object args) {
122 try {
123 return super.invokeMethod(name, args);
124 }
125 catch (GroovyRuntimeException e) {
126
127 Object value = this.getProperty(name);
128 if (value instanceof Closure) {
129 Closure closure = (Closure) value;
130 closure.setDelegate(this);
131 return closure.call(new ParameterArray(args));
132 }
133 else {
134 throw e;
135 }
136 }
137
138 }
139
140 /***
141 * This allows toString to be overridden by a closure <i>field</i> method attached
142 * to the expando object.
143 *
144 * @see java.lang.Object#toString()
145 */
146 public String toString() {
147 Object method = getExpandoProperties().get("toString");
148 if (method != null && method instanceof Closure) {
149
150 Closure closure = (Closure) method;
151 closure.setDelegate(this);
152 return closure.call().toString();
153 } else {
154 return expandoProperties.toString();
155 }
156 }
157
158 /***
159 * This allows equals to be overridden by a closure <i>field</i> method attached
160 * to the expando object.
161 *
162 * @see java.lang.Object#equals(java.lang.Object)
163 */
164 public boolean equals(Object obj) {
165 Object method = getExpandoProperties().get("equals");
166 if (method != null && method instanceof Closure) {
167
168 Closure closure = (Closure) method;
169 closure.setDelegate(this);
170 Boolean ret = (Boolean) closure.call(obj);
171 return ret.booleanValue();
172 } else {
173 return super.equals(obj);
174 }
175 }
176
177 /***
178 * This allows hashCode to be overridden by a closure <i>field</i> method attached
179 * to the expando object.
180 *
181 * @see java.lang.Object#hashCode()
182 */
183 public int hashCode() {
184 Object method = getExpandoProperties().get("hashCode");
185 if (method != null && method instanceof Closure) {
186
187 Closure closure = (Closure) method;
188 closure.setDelegate(this);
189 Integer ret = (Integer) closure.call();
190 return ret.intValue();
191 } else {
192 return super.hashCode();
193 }
194 }
195
196 /***
197 * Factory method to create a new Map used to store the expando properties map
198 * @return a newly created Map implementation
199 */
200 protected Map createMap() {
201 return new HashMap();
202 }
203
204 }