/FileExtraction/Java_unseen/dlitz_resin/modules/ecmascript/src/com/caucho/es/NativeGlobal.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *   Free SoftwareFoundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.es;

import com.caucho.es.parser.Parser;
import com.caucho.vfs.ReadStream;
import com.caucho.vfs.Vfs;

import java.io.IOException;

/**
 * JavaScript object
 */
class NativeGlobal extends Native {
  static final int EVAL = 2;
  static final int PARSE_INT = 3;
  static final int PARSE_FLOAT = 4;
  static final int ESCAPE = 5;
  static final int UNESCAPE = 6;
  static final int IS_NAN = 7;
  static final int IS_FINITE = 8;

  static final int PRINT = 9;
  static final int SYSTEM = PRINT + 1;

  /**
   * Create a new object based on a prototype
   */
  NativeGlobal(String name, int n, int len)
  {
    super(name, len);

    this.n = n;
  }

  /**
   * Creates the native Object object
   */
  static void create(Global resin)
  {
    put(resin, "eval", EVAL, 1);
    put(resin, "parseInt", PARSE_INT, 2);
    put(resin, "parseFloat", PARSE_FLOAT, 1);
    put(resin, "escape", ESCAPE, 1);
    put(resin, "unescape", UNESCAPE, 1);
    put(resin, "isNaN", IS_NAN, 1);
    put(resin, "isFinite", IS_FINITE, 1);

    put(resin, "print", PRINT, 1);
    put(resin, "system", SYSTEM, 1);
  }

  private static void put(Global resin, String name, int n, int len)
  {
    ESId id = ESId.intern(name);

    resin.addProperty(id, new NativeGlobal(name, n, len));
  }

  public ESBase call(Call eval, int length) throws Throwable
  {
    switch (n) {
    case EVAL:
      return eval(eval, length);

    case PARSE_INT:
      return parseInt(eval, length);

    case PARSE_FLOAT:
      if (length < 1)
        return ESNumber.NaN;
      else {
        return ESNumber.create(ESString.parseFloat(eval.getArg(0).toStr()));
      }

    case ESCAPE:
      return escape(eval, length);

    case UNESCAPE:
      return unescape(eval, length);

    case IS_NAN:
      if (length < 1)
        return esUndefined;
      else
        return ESBoolean.create(Double.isNaN(eval.getArg(0).toNum()));

    case IS_FINITE:
      if (length < 1)
        return esUndefined;
      else {
        double value = eval.getArg(0).toNum();
        if (Double.isNaN(value))
          return ESBoolean.create(false);
        else if (value == 1.0/0.0)
          return ESBoolean.create(false);
        else if (value == -1.0/0.0)
          return ESBoolean.create(false);
        else
          return ESBoolean.create(true);
      }

    case PRINT:
      System.out.print(eval.getArg(0).toStr().toString());
      return esNull;

    case SYSTEM:
      String arg = eval.getArg(0).toStr().toString();
      String []args = new String[3];
      Process process;
      try {
        args[0] = "sh";
        args[1] = "-c";
        args[2] = arg;
        process = Runtime.getRuntime().exec(args);
        return ESNumber.create(process.waitFor());
      } catch (Exception e) {
        throw new ESWrapperException(e);
      }

    default:
      throw new ESException("Unknown object function");
    }
  }

  private ESBase eval(Call eval, int length) throws Throwable
  {
    if (length < 1)
      return esUndefined;

    ESBase arg = eval.getArg(0);
    if (! (arg instanceof ESString))
      return arg;

    String string = arg.toString();

    Global resin = Global.getGlobalProto();
    ESBase context = eval.getFunctionContext();
    Script script = null;
    ReadStream is = null;
    try {
      Parser parser = new Parser();
      is = Vfs.openString(string);
      script = parser.parseEval(is, "eval", 1);
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      if (is != null)
        is.close();
    }

    ESCallable jsClass = script.initClass(resin, eval.getGlobal());
    
    return jsClass.call(2, eval.caller, 0);
  }

  private ESBase parseInt(Call eval, int length) throws Throwable
  {
    if (length < 1)
      return ESNumber.NaN;

    ESString string = eval.getArg(0).toStr();
    int len = string.length();

    int i = 0;
    for (; i < len && Character.isSpaceChar(string.charAt(i)); i++) {
    }

    int sign = 1;
    if (i < len && string.charAt(i) == '+') {
      i++;
    } else if (i < len && string.charAt(i) == '-') {
      sign = -1;
      i++;
    }

    int radix = 0;
    if (length > 1) {
      radix = eval.getArg(1).toInt32();
      if (radix == 0) {
      }
      else if (radix < 2 || radix > 36)
        return ESNumber.NaN;
      else if (radix == 16 && i + 1 < length &&
               string.charAt(i) == '0' &&
               (string.charAt(i + 1) == 'x' ||
                string.charAt(i + 1) == 'X'))
        i += 2;
    }
    
    if (radix != 0) {
    }
    else if (i >= len)
      radix = 10;
    else if (string.charAt(i) != '0')
      radix = 10;
    else if (i + 1 < len && 
             (string.charAt(i + 1) == 'x' || string.charAt(i + 1) == 'X')) {
      radix = 16;
      i += 2;
    } else {
      radix = 8;
    }

    long value = 0;
    boolean hasDigit = false;
    for (; i < len; i++) {
      int ch = string.charAt(i);

      if (radix <= 10 && ('0' <= ch && ch <= '0' + radix - 1)) {
        value = radix * value + string.charAt(i) - '0';
        hasDigit = true;
      } else if (radix > 10 && ('0' <= ch && ch <= '9')) {
        value = radix * value + string.charAt(i) - '0';
        hasDigit = true;
      } else if (radix > 10 && ('a' <= ch && ch <= 'a' + radix - 11)) {
        value = radix * value + string.charAt(i) - 'a' + 10;
        hasDigit = true;
      } else if (radix > 10 && ('A' <= ch && ch <= 'A' + radix - 11)) {
        value = radix * value + string.charAt(i) - 'A' + 10;
        hasDigit = true;
      } else
        break;
    }

    if (hasDigit)
      return ESNumber.create((double) sign * value);
    else
      return ESNumber.NaN;
  }

  static ESBase escape(Call eval, int length) throws Throwable
  {
    if (length < 1)
      return esUndefined;
    
    ESString string = eval.getArg(0).toStr();
    StringBuffer sbuf = new StringBuffer();

    for (int i = 0; i < string.length(); i++) {
      int ch = string.charAt(i);

      if (ch >= 'a' && ch <= 'z' ||
          ch >= 'A' && ch <= 'Z' ||
          ch >= '0' && ch <= '9' ||
          ch == '@' || ch == '*' || ch == '.' || ch == '_' ||
          ch == '+' || ch == '-' || ch == '/') {
        sbuf.append((char) ch);
      } else if (ch < 256) {
        sbuf.append('%');
        sbuf.append(Integer.toHexString(ch >> 4));
        sbuf.append(Integer.toHexString(ch & 0xf));
      } else {
        sbuf.append("%u");
        sbuf.append(Integer.toHexString(ch >> 12));
        sbuf.append(Integer.toHexString((ch >> 8) & 0xf));
        sbuf.append(Integer.toHexString((ch >> 4) & 0xf));
        sbuf.append(Integer.toHexString(ch & 0xf));
      }
    } 

    return ESString.create(sbuf.toString());
  }

  static ESBase unescape(Call eval, int length) throws Throwable
  {
    if (length < 1)
      return esUndefined;
    
    ESString string = eval.getArg(0).toStr();
    StringBuffer sbuf = new StringBuffer();

    for (int i = 0; i < string.length(); i++) {
      int ch = string.charAt(i);

      if (ch == '%' && i + 2 < string.length()) {
        int limit = 2;
        int start = 1;

        if (string.charAt(i + 1) == 'u') {
          limit = 4;
          start = 2;
        }

        int newCh = 0;
        int j = 0;
        for (; j < limit && i + j + start < string.length(); j++) {
          if ((ch = string.charAt(i + j + start)) >= '0' && ch <= '9')
            newCh = 16 * newCh + ch - '0';
          else if (ch >= 'a' && ch <= 'f')
            newCh = 16 * newCh + ch - 'a' + 10;
          else if (ch >= 'A' && ch <= 'F')
            newCh = 16 * newCh + ch - 'A' + 10;
          else
            break;
        }

        if (j != limit) {
          sbuf.append('%');
        } else {
          sbuf.append((char) newCh);
          i += limit + start - 1;
        }
      }
      else
        sbuf.append((char) ch);
    }

    return ESString.create(sbuf.toString());
  }
}
