001 /* 002 * $Id: GString.java,v 1.12 2004/05/04 02:54:44 spullara 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 043 import org.codehaus.groovy.runtime.InvokerHelper; 044 045 /** 046 * Represents a String which contains embedded values such as "hello there 047 * ${user} how are you?" which can be evaluated lazily. Advanced users can 048 * iterate over the text and values to perform special processing, such as for 049 * performing SQL operations, the values can be substituted for ? and the 050 * actual value objects can be bound to a JDBC statement. The lovely name of 051 * this class was suggested by Jules Gosnell and was such a good idea, I 052 * couldn't resist :) 053 * 054 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a> 055 * @version $Revision: 1.12 $ 056 */ 057 public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable { 058 059 private Object[] values; 060 061 public GString(Object values) { 062 this.values = (Object[]) values; 063 } 064 065 public GString(Object[] values) { 066 this.values = values; 067 } 068 069 // will be static in an instance 070 public abstract String[] getStrings(); 071 072 /** 073 * Overloaded to implement duck typing for Strings 074 * so that any method that can't be evaluated on this 075 * object will be forwarded to the toString() object instead. 076 */ 077 public Object invokeMethod(String name, Object args) { 078 try { 079 return super.invokeMethod(name, args); 080 } 081 catch (MissingMethodException e) { 082 // lets try invoke the method on the real String 083 return InvokerHelper.invokeMethod(toString(), name, args); 084 } 085 } 086 087 public Object[] getValues() { 088 return values; 089 } 090 091 public GString plus(GString that) { 092 List stringList = new ArrayList(); 093 List valueList = new ArrayList(); 094 095 stringList.addAll(Arrays.asList(getStrings())); 096 valueList.addAll(Arrays.asList(getValues())); 097 098 if (stringList.size() > valueList.size()) { 099 valueList.add(""); 100 } 101 102 stringList.addAll(Arrays.asList(that.getStrings())); 103 valueList.addAll(Arrays.asList(that.getValues())); 104 105 final String[] newStrings = new String[stringList.size()]; 106 stringList.toArray(newStrings); 107 Object[] newValues = valueList.toArray(); 108 109 return new GString(newValues) { 110 public String[] getStrings() { 111 return newStrings; 112 } 113 }; 114 } 115 116 public GString plus(String that) { 117 String[] currentStrings = getStrings(); 118 String[] newStrings = null; 119 Object[] newValues = null; 120 121 newStrings = new String[currentStrings.length + 1]; 122 newValues = new Object[getValues().length + 1]; 123 int lastIndex = currentStrings.length; 124 System.arraycopy(currentStrings, 0, newStrings, 0, lastIndex); 125 System.arraycopy(getValues(), 0, newValues, 0, getValues().length); 126 newStrings[lastIndex] = that; 127 newValues[getValues().length] = ""; 128 129 final String[] finalStrings = newStrings; 130 return new GString(newValues) { 131 132 public String[] getStrings() { 133 return finalStrings; 134 } 135 }; 136 } 137 138 public int getValueCount() { 139 return values.length; 140 } 141 142 public Object getValue(int idx) { 143 return values[idx]; 144 } 145 146 public String toString() { 147 StringWriter buffer = new StringWriter(); 148 try { 149 writeTo(buffer); 150 } 151 catch (IOException e) { 152 throw new StringWriterIOException(e); 153 } 154 return buffer.toString(); 155 } 156 157 public Writer writeTo(Writer out) throws IOException { 158 String[] s = getStrings(); 159 int numberOfValues = values.length; 160 for (int i = 0, size = s.length; i < size; i++) { 161 out.write(s[i]); 162 if (i < numberOfValues) { 163 InvokerHelper.write(out, values[i]); 164 } 165 } 166 return out; 167 } 168 169 public boolean equals(Object that) { 170 if (that instanceof GString) { 171 return equals((GString) that); 172 } 173 return false; 174 } 175 176 public boolean equals(GString that) { 177 return toString().equals(that.toString()); 178 } 179 180 public int hashCode() { 181 return 37 + toString().hashCode(); 182 } 183 184 public int compareTo(Object that) { 185 return toString().compareTo(that.toString()); 186 } 187 188 public char charAt(int index) { 189 return toString().charAt(index); 190 } 191 192 public int length() { 193 return toString().length(); 194 } 195 196 public CharSequence subSequence(int start, int end) { 197 return toString().subSequence(start, end); 198 } 199 }