public VRML97Proto createProto(VRML97ProtoTokenizer stream) throws IOException {
    stream.nextToken();
    String protoName = stream.sval;

    Debug.message("PROTO");
    Debug.message("  name = " + protoName);

    VRML97Proto proto = new VRML97Proto(protoName);

    if (addProtoParameters(proto.getParameterList(), stream) == false) return null;

    int nest = 0;

    stream.nextToken();
    while (stream.ttype != StreamTokenizer.TT_EOF) {
      if (stream.ttype == StreamTokenizer.TT_WORD) {
        if (stream.sval.compareTo("{") == 0) {
          nest++;
          break;
        }
      }
      stream.nextToken();
    }

    stream.nextToken();
    while (stream.ttype != StreamTokenizer.TT_EOF && 0 < nest) {
      switch (stream.ttype) {
        case StreamTokenizer.TT_NUMBER:
          double dvalue = stream.nval;
          String valStr = number2String(dvalue);
          proto.addToken(valStr);
          break;
        case StreamTokenizer.TT_WORD:
          if (stream.sval.compareTo("{") == 0) nest++;
          if (stream.sval.compareTo("}") == 0) nest--;
          if (0 < nest) proto.addToken(stream.sval);
          break;
        case StreamTokenizer.TT_EOL:
          proto.addToken("\n");
          break;
      }
      stream.nextToken();
    }

    return proto;
  }
  public void replace(VRML97ProtoTokenizer stream, PrintWriter printStream) throws IOException {
    int nToken = 0;
    while (stream.nextToken() != StreamTokenizer.TT_EOF) {
      switch (stream.ttype) {
        case StreamTokenizer.TT_NUMBER:
          {
            double numValue = stream.nval;
            printStream.print(number2String(numValue));

            boolean isNextTokenFloatWorld = false;

            stream.nextToken();
            if (stream.ttype == StreamTokenizer.TT_WORD) {
              String strToken = stream.sval;
              if (strToken != null) {
                if (strToken.startsWith("E") || strToken.startsWith("e")) {
                  if (isIntegerValue(numValue) == true) printStream.print(".0");
                  printStream.print(strToken);
                  isNextTokenFloatWorld = true;
                }
              }
            }

            if (isNextTokenFloatWorld == false) stream.pushBack();

            printStream.print(" ");

            nToken++;
          }
          break;
        case StreamTokenizer.TT_WORD:
          {
            String strToken = stream.sval;
            if (strToken.compareTo("PROTO") == 0) {
              VRML97Proto proto = createProto(stream);
              if (proto != null) {
                addVRML97Proto(proto);
                String protoString = proto.getString(null);
                if (protoString != null) {
                  printStream.println(protoString);
                }
              }
            } else if (strToken.compareTo("DEF") == 0) {
              String nodeName = null;
              stream.nextToken();
              if (stream.ttype == StreamTokenizer.TT_WORD) nodeName = stream.sval;
              if (nodeName != null && 0 < nodeName.length())
                printStream.print("DEF " + nodeName + " ");
            } else {
              VRML97Proto proto = getVRML97Proto(strToken);
              if (proto != null) {
                String protoString = getVRML97ProtoString(proto, stream);
                if (protoString != null) {
                  Debug.message(protoString);
                  printStream.println(protoString);
                }
              } else {
                printStream.print(strToken + " ");
                nToken++;
              }
            }
          }
          break;
        case StreamTokenizer.TT_EOL:
          if (0 < nToken) printStream.println("");
          nToken = 0;
          break;
      }
    }

    if (printStream != null) {
      printStream.flush();
      printStream.close();
    }
  }
  public String getVRML97ProtoString(VRML97Proto proto, VRML97ProtoTokenizer stream)
      throws IOException {
    VRML97ProtoParameterList paramList = new VRML97ProtoParameterList();

    int indent = 0;

    stream.nextToken();
    while (stream.ttype != StreamTokenizer.TT_EOF) {
      if (stream.ttype == StreamTokenizer.TT_WORD) {
        if (stream.sval.compareTo("{") == 0) {
          indent++;
          break;
        }
      }
      stream.nextToken();
    }

    if (stream.ttype == StreamTokenizer.TT_EOF) return null;

    stream.nextToken();
    while (stream.ttype != StreamTokenizer.TT_EOF) {
      switch (stream.ttype) {
        case StreamTokenizer.TT_WORD:
          {
            String strToken = stream.sval;
            if (strToken.compareTo("{") == 0) indent++;
            else if (strToken.compareTo("}") == 0) {
              indent--;
              if (indent == 0) return proto.getString(paramList);
            } else {
              String name = strToken;
              VRML97ProtoParameter protoParam = proto.getParameter(name);
              if (protoParam != null) {
                String fieldTypeName = protoParam.getType();
                String value = getParameterValue(fieldTypeName, stream);
                if (hasProto(proto, value) == true) {
                  Debug.message("==== value ==== ");
                  Debug.message(value);
                  StringWriter strWriter = new StringWriter();
                  PrintWriter printWriter = new PrintWriter(strWriter);
                  StringReader strReader = new StringReader(value);
                  VRML97ProtoTokenizer valueStream = new VRML97ProtoTokenizer(strReader);
                  try {
                    replace(valueStream, printWriter);
                    strWriter.flush();
                    strWriter.close();
                  } catch (IOException e) {
                  }
                  value = strWriter.toString();
                  Debug.message("==== New value ==== ");
                  Debug.message(value);
                }
                paramList.addParameter(fieldTypeName, name, value);
              }
            }
          }
          break;
        case StreamTokenizer.TT_EOL:
          {
          }
          break;
      }
      stream.nextToken();
    }

    return null;
  }