import javax.imageio.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.*;
import java.security.NoSuchAlgorithmException;
import java.security.MessageDigest;
import java.lang.reflect.*;
import java.net.*;
import java.io.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.*;
import java.util.concurrent.*;
import java.util.regex.*;
import java.util.List;
import java.util.zip.*;
import java.util.*;


public class main {
  static JTextArea textArea;
  static float fontSize = 24f;
  static String fontSnippet =
    //"#1000970"
    "#1000973" // Classic Trash 1
    ;
  static String thinking = "[...]";
  static String chatID;
  
  public static void main(String[] args) throws Exception {
    chatID = (String) get(getJavaX(), "chatSend_id");
    print("Chat ID: " + chatID);
    
    textArea = new JTextArea();
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    textArea.setFont(loadFont());
    textArea.setMargin(new Insets(10, 10, 10, 10));
    
    textArea.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER && e.getModifiers() == 0) {
          enterPressed();
        }
      }
    });
    
    makeFrame("Dialog 3", new JScrollPane(textArea));
    
    Thread _t = new Thread() {
public void run() {
try {

      while (true) {
        String line = suckFromChatNoHistory();
        if (line == null) break;
        List<String> tok = javaTok(line);
        String text = unquote(tok.get(tok.size()-2));
        String guy = tok.get(1);
        String prefix = chatID + ":";
        if (text.startsWith(prefix))
          answer(text.substring(prefix.length()).trim());
      }
    } catch (Exception _e) {
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
};
_t.setDaemon(true);
_t.start();
    
    sleep(); // important for auto-logging as of now
  }

  static void enterPressed() {
    String text = textArea.getText();
    int idx = textArea.getCaretPosition();
    
    // move to end of line first
    while (idx < text.length() && text.charAt(idx) != '\n') ++idx;
    textArea.setCaretPosition(idx);
    
    //print(quote(text.substring(0, idx)));
    int lineStart = text.lastIndexOf("\n", idx-1);
    //print(lineStart + " " + idx);
    final String line = text.substring(lineStart+1, idx);
    print("?? " + line);
    type(thinking);
    Thread _t = new Thread() {
public void run() {
try {

      processLine(line);
    } catch (Exception _e) {
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
};
_t.start();
  }
  
  static Font loadFont() { try {
 
    return Font.createFont(Font.TRUETYPE_FONT, loadLibrary(fontSnippet)).deriveFont(fontSize);
  
} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

  static void processLine(String line) {
    // nothing to do, just wait for answers
  }
  
  static void answer(final String s) {
    SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {

      print("!! " + s);
      String text = textArea.getText();
      int idx = text.indexOf(thinking);
      if (idx < 0) {
        print("Weird! Maybe user edited?");
        return;
      }
      int cursor = textArea.getCaretPosition();
      textArea.replaceRange(s, idx, idx+thinking.length());
      if (cursor > idx+thinking.length())
        textArea.setCaretPosition(cursor+s.length()-thinking.length());
    } catch (Exception _e) {
  throw _e instanceof RuntimeException ? (RuntimeException) _e : new RuntimeException(_e); } }
});
  }
  
  static void type(String bla) {
    print("!! " + bla);
    textArea.insert("\n" + bla, textArea.getCaretPosition());
  }

static File loadLibrary(String snippetID) {
  return loadBinarySnippet(snippetID);
}

static int suckFromChat_chatPort = 9751;
static Socket suckFromChat_socket;
static BufferedReader suckFromChat_in;
static boolean suckFromChat_present;

static String suckFromChatNoHistory() { try {
 
  while (!suckFromChat_present)
    if (suckFromChat() == null) return null;
  return suckFromChat();

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

static String suckFromChat() { try {
 
  if (suckFromChat_in == null) {
    startChatServerIfNotUp();
    waitForChatServer();
    final Socket s = new Socket("localhost", suckFromChat_chatPort+1);
    print("connected to chat");
    //reportToChat("Hey ho I'm sucking!");
    suckFromChat_in = new BufferedReader(
      new InputStreamReader(s.getInputStream(), "UTF-8"));
  }
  String line = suckFromChat_in.readLine();
  if (line != null && line.length() == 0) suckFromChat_present = true; // We have reached the present :)
  return line;

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

static void suckFromChat_close() { try {
 
  if (suckFromChat_socket != null) {
    suckFromChat_socket.close();
    suckFromChat_socket = null;
  }

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

static Class __javax;

static Class getJavaX() {
  return __javax;
}

static void makeFrame(Component content) {
  // TODO: get program ID
  makeFrame("A JavaX Frame", content);
}

static void makeFrame(String title, Component content) {
  JFrame frame = new JFrame(title);
  frame.getContentPane().add(content);
  frame.setBounds(300, 100, 500, 400);
  frame.setVisible(true);
  exitOnFrameClose(frame);
}

static void sleep(long ms) {
  try {
    Thread.sleep(ms);
  } catch (Exception e) { throw new RuntimeException(e); }
}

static void sleep() { try {
 
  synchronized(main.class) { main.class.wait(); }

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

// replacement for class JavaTok
// maybe incomplete, might want to add floating point numbers
// todo also: extended multi-line strings

static List<String> javaTok(String s) {
  List<String> tok = new ArrayList<String>();
  int l = s.length();
  
  int i = 0;
  while (i < l) {
    int j = i;
    char c; String cc;
    
    // scan for whitespace
    while (j < l) {
      c = s.charAt(j);
      cc = s.substring(j, Math.min(j+2, l));
      if (c == ' ' || c == '\t' || c == '\r' || c == '\n')
        ++j;
      else if (cc.equals("/*")) {
        do ++j; while (j < l && !s.substring(j, Math.min(j+2, l)).equals("*/"));
        j = Math.min(j+2, l);
      } else if (cc.equals("//")) {
        do ++j; while (j < l && "\r\n".indexOf(s.charAt(j)) < 0);
      } else
        break;
    }
    
    tok.add(s.substring(i, j));
    i = j;
    if (i >= l) break;
    c = s.charAt(i); // cc is not needed in rest of loop body
    cc = s.substring(i, Math.min(i+2, l));

    // scan for non-whitespace
    if (c == '\'' || c == '"') {
      char opener = c;
      ++j;
      while (j < l) {
        if (s.charAt(j) == opener) {
          ++j;
          break;
        } else if (s.charAt(j) == '\\' && j+1 < l)
          j += 2;
        else
          ++j;
      }
    } else if (Character.isJavaIdentifierStart(c))
      do ++j; while (j < l && Character.isJavaIdentifierPart(s.charAt(j)));
    else if (Character.isDigit(c))
      do ++j; while (j < l && Character.isDigit(s.charAt(j)));
    else if (cc.equals("[[")) {
      do ++j; while (j+1 < l && !s.substring(j, j+2).equals("]]"));
      j = Math.min(j+2, l);
    } else
      ++j;

    tok.add(s.substring(i, j));
    i = j;
  }
  
  if ((tok.size() % 2) == 0) tok.add("");
  return tok;
}


static void print() {
  System.out.println();
}

static void print(Object o) {
  System.out.println(o);
}

static void print(long i) {
  System.out.println(i);
}

  public static String unquote(String s) {
    if (s.startsWith("[")) {
      int i = 1;
      while (i < s.length() && s.charAt(i) == '=') ++i;
      if (i < s.length() && s.charAt(i) == '[') {
        String m = s.substring(1, i);
        if (s.endsWith("]" + m + "]"))
          return s.substring(i+1, s.length()-i-1);
      }
    }
    
    if (s.startsWith("\"") && s.endsWith("\"") && s.length() > 1) {
      String st = s.substring(1, s.length()-1);
      StringBuilder sb = new StringBuilder(st.length());
  
      for (int i = 0; i < st.length(); i++) {
        char ch = st.charAt(i);
        if (ch == '\\') {
          char nextChar = (i == st.length() - 1) ? '\\' : st
                  .charAt(i + 1);
          // Octal escape?
          if (nextChar >= '0' && nextChar <= '7') {
              String code = "" + nextChar;
              i++;
              if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
                      && st.charAt(i + 1) <= '7') {
                  code += st.charAt(i + 1);
                  i++;
                  if ((i < st.length() - 1) && st.charAt(i + 1) >= '0'
                          && st.charAt(i + 1) <= '7') {
                      code += st.charAt(i + 1);
                      i++;
                  }
              }
              sb.append((char) Integer.parseInt(code, 8));
              continue;
          }
          switch (nextChar) {
          case '\\':
              ch = '\\';
              break;
          case 'b':
              ch = '\b';
              break;
          case 'f':
              ch = '\f';
              break;
          case 'n':
              ch = '\n';
              break;
          case 'r':
              ch = '\r';
              break;
          case 't':
              ch = '\t';
              break;
          case '\"':
              ch = '\"';
              break;
          case '\'':
              ch = '\'';
              break;
          // Hex Unicode: u????
          case 'u':
              if (i >= st.length() - 5) {
                  ch = 'u';
                  break;
              }
              int code = Integer.parseInt(
                      "" + st.charAt(i + 2) + st.charAt(i + 3)
                              + st.charAt(i + 4) + st.charAt(i + 5), 16);
              sb.append(Character.toChars(code));
              i += 5;
              continue;
          }
          i++;
        }
        sb.append(ch);
      }
      return sb.toString();      
    } else
      return s; // return original
  }

static Object get(Object o, String field) {
  if (o instanceof Class) return get((Class) o, field);
  try {
    Field f = get_findField(o.getClass(), field);
    f.setAccessible(true);
    return f.get(o);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

static Object get(Class c, String field) {
  try {
    Field f = get_findStaticField(c, field);
    f.setAccessible(true);
    return f.get(null);
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

static Field get_findStaticField(Class<?> c, String field) {
  for (Field f : c.getDeclaredFields())
    if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
      return f;
  throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
}

static Field get_findField(Class<?> c, String field) {
  for (Field f : c.getDeclaredFields())
    if (f.getName().equals(field))
      return f;
  throw new RuntimeException("Field '" + field + "' not found in " + c.getName());
}

static void waitForChatServer() {
  if (!portIsBound(9751)) {
    System.out.print("Waiting for chat server... ");
    do {
      sleep(1000);
    } while (!portIsBound(9751));
    print("OK.");
  }
}

static void startChatServerIfNotUp() {
  if (portIsBound(9751)) {
    //print("Chat seems to be up.");
  } else {
    nohupJavax("1000867");
    print("Chat server should be coming up any minute now.");
  }
}

static File loadBinarySnippet(String snippetID) { try {
 
  long id = parseSnippetID(snippetID);
  File f = DiskSnippetCache_getLibrary(id);
  if (f == null) {
    byte[] data = loadDataSnippetImpl(snippetID);
    DiskSnippetCache_putLibrary(id, data);
    f = DiskSnippetCache_getLibrary(id);
  }
  return f;

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

  static void exitOnFrameClose(JFrame frame) {
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  }


static boolean portIsBound(int port) {
  try {
    ServerSocket s = new ServerSocket(port);
    s.close();
    return false;
  } catch (IOException e) {
    return true;
  }
}

static void nohupJavax(String javaxargs) { try {
 
  File xfile = new File(userHome(), ".javax/x24.jar");
  if (!xfile.isFile()) {
    String url = "http://tinybrain.de/x24.jar";
    byte[] data = loadBinaryPage(url);
    if (data.length < 1000000)
      fail("Could not load " + url);
    saveBinaryFile(xfile.getPath(), data);
  }
  String jarPath = xfile.getPath();
  nohup("java -jar " + (isWindows() ? winQuote(jarPath) : bashQuote(jarPath)) + " " + javaxargs);

} catch (Throwable __e) { throw __e instanceof RuntimeException ? (RuntimeException) __e : new RuntimeException(__e); }}

  
  // Data files are immutable, use centralized cache
public static File DiskSnippetCache_getLibrary(long snippetID) throws IOException {
  File file = new File(getGlobalCache(), "data_" + snippetID + ".jar");
  return file.exists() ? file : null;
}

public static void DiskSnippetCache_putLibrary(long snippetID, byte[] data) throws IOException {
  saveBinaryFile(new File(getGlobalCache(), "data_" + snippetID).getPath() + ".jar", data);
}

static byte[] loadDataSnippetImpl(String snippetID) throws IOException {
  byte[] data;
  try {
    URL url = new URL("http://eyeocr.sourceforge.net/filestore/filestore.php?cmd=serve&file=blob_"
      + parseSnippetID(snippetID) + "&contentType=application/binary");
    System.err.println("Loading library: " + url);
    data = loadBinaryPage(url.openConnection());
    System.err.println("Bytes loaded: " + data.length);
  } catch (FileNotFoundException e) {
    throw new IOException("Binary snippet #" + snippetID + " not found or not public");
  }
  return data;
}

public static long parseSnippetID(String snippetID) {
  return Long.parseLong(shortenSnippetID(snippetID));
}

/** possibly improvable */
public static String bashQuote(String text) {
  if (text == null) return null;
  return "\"" + text
    .replace("\\", "\\\\")
    .replace("\"", "\\\"")
    .replace("\n", "\\n")
    .replace("\r", "\\r") + "\"";
}


/** possibly improvable */
public static String winQuote(String text) {
  if (text == null) return null;
  return "\"" + text
    .replace("\\", "\\\\")
    .replace("\"", "\\\"")
    .replace("\n", "\\n")
    .replace("\r", "\\r") + "\"";
}


public static boolean isWindows() {
  return System.getProperty("os.name").contains("Windows");
}

public static File nohup(String cmd) throws IOException {
  File outFile = File.createTempFile("nohup_" + nohup_sanitize(cmd), ".out");
  nohup(cmd, outFile, false);
  return outFile;
}

static String nohup_sanitize(String s) {
  return s.replaceAll("[^a-zA-Z0-9\\-_]", "");
}

/** outFile takes stdout and stderr. */
public static void nohup(String cmd, File outFile, boolean append) throws IOException {
  String command = nohup_makeNohupCommand(cmd, outFile, append);

  File scriptFile = File.createTempFile("_realnohup", isWindows() ? ".bat" : "");
  System.out.println("[Nohup] " + command);
  try {
    //System.out.println("[RealNohup] Script file: " + scriptFile.getPath());
    saveTextFile(scriptFile.getPath(), command);
    String[] command2;
    if (isWindows())
      command2 = new String[] {"cmd", "/c", "start", "/b", scriptFile.getPath() };
    else
      command2 = new String[] {"/bin/bash", scriptFile.getPath() };
      
    Process process = Runtime.getRuntime().exec(command2);
    try {
      process.waitFor();
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    int value = process.exitValue();
    //System.out.println("exit value: " + value);
  } finally {
    if (!isWindows())
      scriptFile.delete();
  }
}

public static String nohup_makeNohupCommand(String cmd, File outFile, boolean append) {
  mkdirsForFile(outFile);

  String command;
  if (isWindows())
    command = cmd + (append ? " >>" : " >") + winQuote(outFile.getPath()) + " 2>&1";
  else
    command = "nohup " + cmd + (append ? " >>" : " >") + bashQuote(outFile.getPath()) + " 2>&1 &";
  return command;
}


static File getGlobalCache() {
  File file = new File(userHome(), ".tinybrain/snippet-cache");
  file.mkdirs();
  return file;
}


static byte[] loadBinaryPage(String url) throws IOException {
  return loadBinaryPage(new URL(url).openConnection());
}

public static byte[] loadBinaryPage(URLConnection con) throws IOException {
  //setHeaders(con);
  ByteArrayOutputStream buf = new ByteArrayOutputStream();
  InputStream inputStream = con.getInputStream();
  int n = 0;
  while (true) {
    int ch = inputStream.read();
    if (ch < 0)
      break;
    buf.write(ch);
    if (++n % 100000 == 0)
      System.err.println("  " + n + " bytes loaded.");
  }
  inputStream.close();
  return buf.toByteArray();
}


  /** writes safely (to temp file, then rename) */
  public static void saveBinaryFile(String fileName, byte[] contents) throws IOException {
    File file = new File(fileName);
    File parentFile = file.getParentFile();
    if (parentFile != null)
      parentFile.mkdirs();
    String tempFileName = fileName + "_temp";
    FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
    fileOutputStream.write(contents);
    fileOutputStream.close();
    if (file.exists() && !file.delete())
      throw new IOException("Can't delete " + fileName);

    if (!new File(tempFileName).renameTo(file))
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
  }


  static RuntimeException fail() {
    throw new RuntimeException("fail");
  }
  
  static RuntimeException fail(String msg) {
    throw new RuntimeException(msg);
  }

static String _userHome;
static String userHome() {
  if (_userHome == null) {
    if (isAndroid())
      _userHome = "/storage/sdcard0/";
    else
      _userHome = System.getProperty("user.home");
    //System.out.println("userHome: " + _userHome);
  }
  return _userHome;
}


static String shortenSnippetID(String snippetID) {
  if (snippetID.startsWith("#"))
    snippetID = snippetID.substring(1);
  String httpBlaBla = "http://tinybrain.de/";
  if (snippetID.startsWith(httpBlaBla))
    snippetID = snippetID.substring(httpBlaBla.length());
  return snippetID;
}

static boolean isAndroid() { return System.getProperty("java.vendor").toLowerCase().indexOf("android") >= 0; }

public static void mkdirsForFile(File file) {
  File dir = file.getParentFile();
  if (dir != null) // is null if file is in current dir
    dir.mkdirs();
}


static Object process(String processorID, Object in) {
  return process(processorID, in, "in");
}

static Object process(String processorID, Object in, String outVar) {
  try {
    Class processor = hotwire(processorID);
    set(processor, "in", in);
    call(processor, "main", new Object[] {new String[0]});
    return get(processor, outVar);
  } catch (Exception e) {
    throw new RuntimeException("Error in #" + parseSnippetID(processorID), e);
  }
}

  /** writes safely (to temp file, then rename) */
  public static void saveTextFile(String fileName, String contents) throws IOException {
    File file = new File(fileName);
    File parentFile = file.getParentFile();
    if (parentFile != null)
      parentFile.mkdirs();
    String tempFileName = fileName + "_temp";
    FileOutputStream fileOutputStream = new FileOutputStream(tempFileName);
    OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "UTF-8");
    PrintWriter printWriter = new PrintWriter(outputStreamWriter);
    printWriter.print(contents);
    printWriter.close();
    if (file.exists() && !file.delete())
      throw new IOException("Can't delete " + fileName);

    if (!new File(tempFileName).renameTo(file))
      throw new IOException("Can't rename " + tempFileName + " to " + fileName);
  }

  static Object call(Object o, String method, Object... args) {
    try {
      if (o instanceof Class) {
        Method m = call_findStaticMethod((Class) o, method, args, false);
        m.setAccessible(true);
        return m.invoke(null, args);
      } else {
        Method m = call_findMethod(o, method, args, false);
        m.setAccessible(true);
        return m.invoke(o, args);
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  static Method call_findStaticMethod(Class c, String method, Object[] args, boolean debug) {
    Class _c = c;
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) {
        if (debug)
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
        if (!m.getName().equals(method)) {
          if (debug) System.out.println("Method name mismatch: " + method);
          continue;
        }

        if ((m.getModifiers() & Modifier.STATIC) == 0 || !call_checkArgs(m, args, debug))
          continue;

        return m;
      }
      c = c.getSuperclass();
    }
    throw new RuntimeException("Method '" + method + "' (static) with " + args.length + " parameter(s) not found in " + _c.getName());
  }

  static Method call_findMethod(Object o, String method, Object[] args, boolean debug) {
    Class c = o.getClass();
    while (c != null) {
      for (Method m : c.getDeclaredMethods()) {
        if (debug)
          System.out.println("Checking method " + m.getName() + " with " + m.getParameterTypes().length + " parameters");;
        if (m.getName().equals(method) && call_checkArgs(m, args, debug))
          return m;
      }
      c = c.getSuperclass();
    }
    throw new RuntimeException("Method '" + method + "' (non-static) with " + args.length + " parameter(s) not found in " + o.getClass().getName());
  }

  private static boolean call_checkArgs(Method m, Object[] args, boolean debug) {
    Class<?>[] types = m.getParameterTypes();
    if (types.length != args.length) {
      if (debug)
        System.out.println("Bad parameter length: " + args.length + " vs " + types.length);
      return false;
    }
    for (int i = 0; i < types.length; i++)
      if (!(args[i] == null || isInstanceX(types[i], args[i]))) {
        if (debug)
          System.out.println("Bad parameter " + i + ": " + args[i] + " vs " + types[i]);
        return false;
      }
    return true;
  }



  // compile JavaX source, load classes & return main class
  // src can be a snippet ID or actual source code
  
  static Class<?> hotwire(String src) {
    try {
      Class _javax = getJavaX();

      List<File> libraries = new ArrayList<File>();
      File srcDir = (File) call(_javax, "transpileMain", src, libraries);
      
      Object androidContext = get(_javax, "androidContext");
      if (androidContext != null)
        return (Class) call(_javax, "loadx2android", srcDir, src);
        
      File classesDir = (File) call(_javax, "TempDirMaker_make");
      String javacOutput = (String) call(_javax, "compileJava", srcDir, libraries, classesDir);
      System.out.println(javacOutput);
      
      URL[] urls = new URL[libraries.size()+1];
      urls[0] = classesDir.toURI().toURL();
      for (int i = 0; i < libraries.size(); i++)
        urls[i+1] = libraries.get(i).toURI().toURL();

      // make class loader
      URLClassLoader classLoader = new URLClassLoader(urls);
  
      // load & return main class
      Class<?> theClass = classLoader.loadClass("main");
      
      call(_javax, "setVars", theClass, isSnippetID(src) ? src: null);

      return theClass;
    } catch (Exception e) {
      throw e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e);
    }
  }

  static void set(Class c, String field, Object value) {
    try {
      Field f = set_findStaticField(c, field);
      f.setAccessible(true);
      f.set(null, value);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }
  
  static Field set_findStaticField(Class<?> c, String field) {
    for (Field f : c.getDeclaredFields())
      if (f.getName().equals(field) && (f.getModifiers() & Modifier.STATIC) != 0)
        return f;
    throw new RuntimeException("Static field '" + field + "' not found in " + c.getName());
  }

// extended over Class.isInstance() to handle primitive types
private static boolean isInstanceX(Class type, Object arg) {
  if (type == boolean.class) return arg instanceof Boolean;
  if (type == int.class) return arg instanceof Integer;
  if (type == long.class) return arg instanceof Long;
  if (type == float.class) return arg instanceof Float;
  if (type == short.class) return arg instanceof Short;
  if (type == char.class) return arg instanceof Character;
  if (type == byte.class) return arg instanceof Byte;
  return type.isInstance(arg);
}

  public static boolean isSnippetID(String s) {
    try {
      parseSnippetID(s);
      return true;
    } catch (RuntimeException e) {
      return false;
    }
  }
}