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 package groovy.lang;
35
36 import java.io.IOException;
37 import java.io.StringWriter;
38 import java.io.Writer;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.regex.Pattern;
43
44 import org.codehaus.groovy.runtime.InvokerHelper;
45
46 /***
47 * Represents a String which contains embedded values such as "hello there
48 * ${user} how are you?" which can be evaluated lazily. Advanced users can
49 * iterate over the text and values to perform special processing, such as for
50 * performing SQL operations, the values can be substituted for ? and the
51 * actual value objects can be bound to a JDBC statement. The lovely name of
52 * this class was suggested by Jules Gosnell and was such a good idea, I
53 * couldn't resist :)
54 *
55 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
56 * @version $Revision: 1.16 $
57 */
58 public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable, Buildable {
59
60 private Object[] values;
61
62 public GString(Object values) {
63 this.values = (Object[]) values;
64 }
65
66 public GString(Object[] values) {
67 this.values = values;
68 }
69
70
71 public abstract String[] getStrings();
72
73 /***
74 * Overloaded to implement duck typing for Strings
75 * so that any method that can't be evaluated on this
76 * object will be forwarded to the toString() object instead.
77 */
78 public Object invokeMethod(String name, Object args) {
79 try {
80 return super.invokeMethod(name, args);
81 }
82 catch (MissingMethodException e) {
83
84 return InvokerHelper.invokeMethod(toString(), name, args);
85 }
86 }
87
88 public Object[] getValues() {
89 return values;
90 }
91
92 public GString plus(GString that) {
93 List stringList = new ArrayList();
94 List valueList = new ArrayList();
95
96 stringList.addAll(Arrays.asList(getStrings()));
97 valueList.addAll(Arrays.asList(getValues()));
98
99 if (stringList.size() > valueList.size()) {
100 valueList.add("");
101 }
102
103 stringList.addAll(Arrays.asList(that.getStrings()));
104 valueList.addAll(Arrays.asList(that.getValues()));
105
106 final String[] newStrings = new String[stringList.size()];
107 stringList.toArray(newStrings);
108 Object[] newValues = valueList.toArray();
109
110 return new GString(newValues) {
111 public String[] getStrings() {
112 return newStrings;
113 }
114 };
115 }
116
117 public GString plus(String that) {
118 String[] currentStrings = getStrings();
119 String[] newStrings = null;
120 Object[] newValues = null;
121
122 newStrings = new String[currentStrings.length + 1];
123 newValues = new Object[getValues().length + 1];
124 int lastIndex = currentStrings.length;
125 System.arraycopy(currentStrings, 0, newStrings, 0, lastIndex);
126 System.arraycopy(getValues(), 0, newValues, 0, getValues().length);
127 newStrings[lastIndex] = that;
128 newValues[getValues().length] = "";
129
130 final String[] finalStrings = newStrings;
131 return new GString(newValues) {
132
133 public String[] getStrings() {
134 return finalStrings;
135 }
136 };
137 }
138
139 public int getValueCount() {
140 return values.length;
141 }
142
143 public Object getValue(int idx) {
144 return values[idx];
145 }
146
147 public String toString() {
148 StringWriter buffer = new StringWriter();
149 try {
150 writeTo(buffer);
151 }
152 catch (IOException e) {
153 throw new StringWriterIOException(e);
154 }
155 return buffer.toString();
156 }
157
158 public Writer writeTo(Writer out) throws IOException {
159 String[] s = getStrings();
160 int numberOfValues = values.length;
161 for (int i = 0, size = s.length; i < size; i++) {
162 out.write(s[i]);
163 if (i < numberOfValues) {
164 InvokerHelper.write(out, values[i]);
165 }
166 }
167 return out;
168 }
169
170
171
172
173 public void build(final GroovyObject builder) {
174 final String[] s = getStrings();
175 final int numberOfValues = values.length;
176
177 for (int i = 0, size = s.length; i < size; i++) {
178 builder.getProperty("mkp");
179 builder.invokeMethod("yield", new Object[]{s[i]});
180 if (i < numberOfValues) {
181 builder.getProperty("mkp");
182 builder.invokeMethod("yield", new Object[]{values[i]});
183 }
184 }
185 }
186
187 public boolean equals(Object that) {
188 if (that instanceof GString) {
189 return equals((GString) that);
190 }
191 return false;
192 }
193
194 public boolean equals(GString that) {
195 return toString().equals(that.toString());
196 }
197
198 public int hashCode() {
199 return 37 + toString().hashCode();
200 }
201
202 public int compareTo(Object that) {
203 return toString().compareTo(that.toString());
204 }
205
206 public char charAt(int index) {
207 return toString().charAt(index);
208 }
209
210 public int length() {
211 return toString().length();
212 }
213
214 public CharSequence subSequence(int start, int end) {
215 return toString().subSequence(start, end);
216 }
217
218 /***
219 * Turns a String into a regular expression pattern
220 *
221 * @return the regular expression pattern
222 */
223 public Pattern negate() {
224 return InvokerHelper.regexPattern(toString());
225 }
226 }