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