001 /* 002 * $Id: GString.java 4098 2006-10-10 16:09:48Z blackdrag $ 003 * 004 * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved. 005 * 006 * Redistribution and use of this software and associated documentation 007 * ("Software"), with or without modification, are permitted provided that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 package groovy.lang; 035 036 import java.io.IOException; 037 import java.io.StringWriter; 038 import java.io.Writer; 039 import java.util.ArrayList; 040 import java.util.Arrays; 041 import java.util.List; 042 import java.util.regex.Pattern; 043 044 import org.codehaus.groovy.runtime.DefaultGroovyMethods; 045 import org.codehaus.groovy.runtime.InvokerHelper; 046 047 /** 048 * Represents a String which contains embedded values such as "hello there 049 * ${user} how are you?" which can be evaluated lazily. Advanced users can 050 * iterate over the text and values to perform special processing, such as for 051 * performing SQL operations, the values can be substituted for ? and the 052 * actual value objects can be bound to a JDBC statement. The lovely name of 053 * this class was suggested by Jules Gosnell and was such a good idea, I 054 * couldn't resist :) 055 * 056 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 057 * @version $Revision: 4098 $ 058 */ 059 public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable, Buildable { 060 061 private Object[] values; 062 063 public GString(Object values) { 064 this.values = (Object[]) values; 065 } 066 067 public GString(Object[] values) { 068 this.values = values; 069 } 070 071 // will be static in an instance 072 public abstract String[] getStrings(); 073 074 /** 075 * Overloaded to implement duck typing for Strings 076 * so that any method that can't be evaluated on this 077 * object will be forwarded to the toString() object instead. 078 */ 079 public Object invokeMethod(String name, Object args) { 080 try { 081 return super.invokeMethod(name, args); 082 } 083 catch (MissingMethodException e) { 084 // lets try invoke the method on the real String 085 return InvokerHelper.invokeMethod(toString(), name, args); 086 } 087 } 088 089 public Object[] getValues() { 090 return values; 091 } 092 093 public GString plus(GString that) { 094 List stringList = new ArrayList(); 095 List valueList = new ArrayList(); 096 097 stringList.addAll(Arrays.asList(getStrings())); 098 valueList.addAll(Arrays.asList(getValues())); 099 100 if (stringList.size() > valueList.size()) { 101 valueList.add(""); 102 } 103 104 stringList.addAll(Arrays.asList(that.getStrings())); 105 valueList.addAll(Arrays.asList(that.getValues())); 106 107 final String[] newStrings = new String[stringList.size()]; 108 stringList.toArray(newStrings); 109 Object[] newValues = valueList.toArray(); 110 111 return new GString(newValues) { 112 public String[] getStrings() { 113 return newStrings; 114 } 115 }; 116 } 117 118 public GString plus(String that) { 119 String[] currentStrings = getStrings(); 120 String[] newStrings = null; 121 Object[] newValues = null; 122 123 newStrings = new String[currentStrings.length + 1]; 124 newValues = new Object[getValues().length + 1]; 125 int lastIndex = currentStrings.length; 126 System.arraycopy(currentStrings, 0, newStrings, 0, lastIndex); 127 System.arraycopy(getValues(), 0, newValues, 0, getValues().length); 128 newStrings[lastIndex] = that; 129 newValues[getValues().length] = ""; 130 131 final String[] finalStrings = newStrings; 132 return new GString(newValues) { 133 134 public String[] getStrings() { 135 return finalStrings; 136 } 137 }; 138 } 139 140 public int getValueCount() { 141 return values.length; 142 } 143 144 public Object getValue(int idx) { 145 return values[idx]; 146 } 147 148 public String toString() { 149 StringWriter buffer = new StringWriter(); 150 try { 151 writeTo(buffer); 152 } 153 catch (IOException e) { 154 throw new StringWriterIOException(e); 155 } 156 return buffer.toString(); 157 } 158 159 public Writer writeTo(Writer out) throws IOException { 160 String[] s = getStrings(); 161 int numberOfValues = values.length; 162 for (int i = 0, size = s.length; i < size; i++) { 163 out.write(s[i]); 164 if (i < numberOfValues) { 165 InvokerHelper.write(out, values[i]); 166 } 167 } 168 return out; 169 } 170 171 /* (non-Javadoc) 172 * @see groovy.lang.Buildable#build(groovy.lang.GroovyObject) 173 */ 174 public void build(final GroovyObject builder) { 175 final String[] s = getStrings(); 176 final int numberOfValues = values.length; 177 178 for (int i = 0, size = s.length; i < size; i++) { 179 builder.getProperty("mkp"); 180 builder.invokeMethod("yield", new Object[]{s[i]}); 181 if (i < numberOfValues) { 182 builder.getProperty("mkp"); 183 builder.invokeMethod("yield", new Object[]{values[i]}); 184 } 185 } 186 } 187 188 public boolean equals(Object that) { 189 if (that instanceof GString) { 190 return equals((GString) that); 191 } 192 return false; 193 } 194 195 public boolean equals(GString that) { 196 return toString().equals(that.toString()); 197 } 198 199 public int hashCode() { 200 return 37 + toString().hashCode(); 201 } 202 203 public int compareTo(Object that) { 204 return toString().compareTo(that.toString()); 205 } 206 207 public char charAt(int index) { 208 return toString().charAt(index); 209 } 210 211 public int length() { 212 return toString().length(); 213 } 214 215 public CharSequence subSequence(int start, int end) { 216 return toString().subSequence(start, end); 217 } 218 219 /** 220 * Turns a String into a regular expression pattern 221 * 222 * @return the regular expression pattern 223 */ 224 public Pattern negate() { 225 return DefaultGroovyMethods.negate(toString()); 226 } 227 }