// <TYPE> <NAME> [ "(" <FFBINDING> ")" ] [-LINEAR] [ ":" <DEFAULTVAL> ]
  private void readParam(String statement) throws IOException {
    String name;
    String defaultVal = null;
    ColorSpace colorSpace = null;

    String[] split = statement.split(":");

    // Parse default val
    if (split.length == 1) {
      // Doesn't contain default value
    } else {
      if (split.length != 2) {
        throw new IOException("Parameter statement syntax incorrect");
      }
      statement = split[0].trim();
      defaultVal = split[1].trim();
    }

    if (statement.endsWith("-LINEAR")) {
      colorSpace = ColorSpace.Linear;
      statement = statement.substring(0, statement.length() - "-LINEAR".length());
    }

    // Parse ffbinding
    int startParen = statement.indexOf("(");
    if (startParen != -1) {
      // get content inside parentheses
      int endParen = statement.indexOf(")", startParen);
      String bindingStr = statement.substring(startParen + 1, endParen).trim();
      // don't care about bindingStr
      statement = statement.substring(0, startParen);
    }

    // Parse type + name
    split = statement.split(whitespacePattern);
    if (split.length != 2) {
      throw new IOException("Parameter statement syntax incorrect");
    }

    VarType type;
    if (split[0].equals("Color")) {
      type = VarType.Vector4;
    } else {
      type = VarType.valueOf(split[0]);
    }

    name = split[1];

    Object defaultValObj = null;
    if (defaultVal != null) {
      defaultValObj = readValue(type, defaultVal);
    }
    if (type.isTextureType()) {
      materialDef.addMaterialParamTexture(type, name, colorSpace);
    } else {
      materialDef.addMaterialParam(type, name, defaultValObj);
    }
  }
 private Object readValue(final VarType type, final String value) throws IOException {
   if (type.isTextureType()) {
     return parseTextureType(type, value);
   } else {
     String[] split = value.trim().split(whitespacePattern);
     switch (type) {
       case Float:
         if (split.length != 1) {
           throw new IOException("Float value parameter must have 1 entry: " + value);
         }
         return Float.parseFloat(split[0]);
       case Vector2:
         if (split.length != 2) {
           throw new IOException("Vector2 value parameter must have 2 entries: " + value);
         }
         return new Vector2f(Float.parseFloat(split[0]), Float.parseFloat(split[1]));
       case Vector3:
         if (split.length != 3) {
           throw new IOException("Vector3 value parameter must have 3 entries: " + value);
         }
         return new Vector3f(
             Float.parseFloat(split[0]), Float.parseFloat(split[1]), Float.parseFloat(split[2]));
       case Vector4:
         if (split.length != 4) {
           throw new IOException("Vector4 value parameter must have 4 entries: " + value);
         }
         return new ColorRGBA(
             Float.parseFloat(split[0]),
             Float.parseFloat(split[1]),
             Float.parseFloat(split[2]),
             Float.parseFloat(split[3]));
       case Int:
         if (split.length != 1) {
           throw new IOException("Int value parameter must have 1 entry: " + value);
         }
         return Integer.parseInt(split[0]);
       case Boolean:
         if (split.length != 1) {
           throw new IOException("Boolean value parameter must have 1 entry: " + value);
         }
         return Boolean.parseBoolean(split[0]);
       default:
         throw new UnsupportedOperationException("Unknown type: " + type);
     }
   }
 }