"$System.in"

で入力が取れる仕組みを今更ながら追ってみた。

// groovy-1.5.7/src/main/groovy/lang/GString.java 148-157
  public String toString() {
    StringWriter buffer = new StringWriter();
    try {
      writeTo(buffer);
    }
    catch (IOException e) {
      throw new StringWriterIOException(e);
    }
    return buffer.toString();
  }
  • toString は writeTo を使用。
// groovy-1.5.7/src/main/groovy/lang/GString.java 159-184
  public Writer writeTo(Writer out) throws IOException {
    String[] s = getStrings();
    int numberOfValues = values.length;
    for (int i = 0, size = s.length; i < size; i++) {
      out.write(s[i]);
      if (i < numberOfValues) {
        final Object value = values[i];

        if (value instanceof Closure) {
          final Closure c = (Closure)value;

          if (c.getMaximumNumberOfParameters() == 0) {
            InvokerHelper.write(out, c.call(null));
          } else if (c.getMaximumNumberOfParameters() == 1) {
            c.call(new Object[]{out});
          } else {
            throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
                + c.getMaximumNumberOfParameters() + " parameters");
          }
        } else {
          InvokerHelper.write(out, value);
        }
      }
    }
    return out;
  }
  • strings と values($の部分) から交互に書く。
  • 値が一引数のクロージャには Writer を渡して戻り値は使わない。
  • クロージャで無ければ InvokerHelper に移譲。
// groovy-1.5.7/src/main/org/codehaus/groovy/runtime/InvokerHelper.java 474-503
  public static void write(Writer out, Object object) throws IOException {
    if (object instanceof String) {
      out.write((String) object);
    } else if (object instanceof Object[]) {
      out.write(toArrayString((Object[]) object));
    } else if (object instanceof Map) {
      out.write(toMapString((Map) object));
    } else if (object instanceof Collection) {
      out.write(toListString((Collection) object));
    } else if (object instanceof Writable) {
      Writable writable = (Writable) object;
      writable.writeTo(out);
    } else if (object instanceof InputStream || object instanceof Reader) {
      // Copy stream to stream
      Reader reader;
      if (object instanceof InputStream) {
        reader = new InputStreamReader((InputStream) object);
      } else {
        reader = (Reader) object;
      }
      char[] chars = new char[8192];
      int i;
      while ((i = reader.read(chars)) != -1) {
        out.write(chars, 0, i);
      }
      reader.close();
    } else {
      out.write(toString(object));
    }
  }
  • InputStream と Reader に対応している。読んで書いて閉じる*1
  • Writable なら 書かせるだけ。
def c = { it << 'hoge' }
def w = [writeTo: c] as Writable;
assert "$c $w" == 'hoge hoge'

*1:このため「"$System.in"」は再利用が効かず,二回以上使う場合は「System.in.text」で妥協する。