001 /* 002 * $Id: GString.java,v 1.16 2005/11/04 08:33:57 tug Exp $ 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.InvokerHelper; 045 046 /** 047 * Represents a String which contains embedded values such as "hello there 048 * ${user} how are you?" which can be evaluated lazily. Advanced users can 049 * iterate over the text and values to perform special processing, such as for 050 * performing SQL operations, the values can be substituted for ? and the 051 * actual value objects can be bound to a JDBC statement. The lovely name of 052 * this class was suggested by Jules Gosnell and was such a good idea, I 053 * couldn't resist :) 054 * 055 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 056 * @version $Revision: 1.16 $ 057 */ 058 public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable, Buildable { 059 060 private Object[] values; 061 062 public GString(Object values) { 063 this.values = (Object[]) values; 064 } 065 066 public GString(Object[] values) { 067 this.values = values; 068 } 069 070 // will be static in an instance 071 public abstract String[] getStrings(); 072 073 /** 074 * Overloaded to implement duck typing for Strings 075 * so that any method that can't be evaluated on this 076 * object will be forwarded to the toString() object instead. 077 */ 078 public Object invokeMethod(String name, Object args) { 079 try { 080 return super.invokeMethod(name, args); 081 } 082 catch (MissingMethodException e) { 083 // lets try invoke the method on the real String 084 return InvokerHelper.invokeMethod(toString(), name, args); 085 } 086 } 087 088 public Object[] getValues() { 089 return values; 090 } 091 092 public GString plus(GString that) { 093 List stringList = new ArrayList(); 094 List valueList = new ArrayList(); 095 096 stringList.addAll(Arrays.asList(getStrings())); 097 valueList.addAll(Arrays.asList(getValues())); 098 099 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 /* (non-Javadoc) 171 * @see groovy.lang.Buildable#build(groovy.lang.GroovyObject) 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 }