示例#1
0
  /**
   * Takes a packed-stream InputStream, and writes to a JarOutputStream. Internally the entire
   * buffer must be read, it may be more efficient to read the packed-stream to a file and pass the
   * File object, in the alternate method described below.
   *
   * <p>Closes its input but not its output. (The output can accumulate more elements.)
   *
   * @param in an InputStream.
   * @param out a JarOutputStream.
   * @exception IOException if an error is encountered.
   */
  public void unpack(InputStream in0, JarOutputStream out) throws IOException {
    assert (Utils.currentInstance.get() == null);
    TimeZone tz = (_props.getBoolean(Utils.PACK_DEFAULT_TIMEZONE)) ? null : TimeZone.getDefault();

    try {
      Utils.currentInstance.set(this);
      if (tz != null) TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
      final int verbose = _props.getInteger(Utils.DEBUG_VERBOSE);
      BufferedInputStream in = new BufferedInputStream(in0);
      if (Utils.isJarMagic(Utils.readMagic(in))) {
        if (verbose > 0) Utils.log.info("Copying unpacked JAR file...");
        Utils.copyJarFile(new JarInputStream(in), out);
      } else if (_props.getBoolean(Utils.DEBUG_DISABLE_NATIVE)) {
        (new DoUnpack()).run(in, out);
        in.close();
        Utils.markJarFile(out);
      } else {
        (new NativeUnpack(this)).run(in, out);
        in.close();
        Utils.markJarFile(out);
      }
    } finally {
      _nunp = null;
      Utils.currentInstance.set(null);
      if (tz != null) TimeZone.setDefault(tz);
    }
  }
示例#2
0
 public static String getCurrentUTCDateTime() {
   try {
     return date2UTC(new Date(), TimeZone.getDefault());
   } catch (UtilitiesException ex) {
     return null;
   }
 }
  /** Returns the time zone for which this <code>CronExpression</code> will be resolved. */
  public TimeZone getTimeZone() {
    if (timeZone == null) {
      timeZone = TimeZone.getDefault();
    }

    return timeZone;
  }
示例#4
0
 private static Timestamp shift(Timestamp v) {
   if (v == null) {
     return null;
   }
   long time = v.getTime();
   int offset = TimeZone.getDefault().getOffset(time);
   return new Timestamp(time + offset);
 }
示例#5
0
 private static Time shift(Time v) {
   if (v == null) {
     return null;
   }
   long time = v.getTime();
   int offset = TimeZone.getDefault().getOffset(time);
   return new Time((time + offset) % DateTimeUtil.MILLIS_PER_DAY);
 }
示例#6
0
 private static Date shift(Date v) {
   if (v == null) {
     return null;
   }
   long time = v.getTime();
   int offset = TimeZone.getDefault().getOffset(time);
   return new Date(time + offset);
 }
 protected Comment getGeneratedBy(boolean timestamp) {
   String text = "Generated by javadoc"; // marker string, deliberately not localized
   if (timestamp) {
     Calendar calendar = new GregorianCalendar(TimeZone.getDefault());
     Date today = calendar.getTime();
     text += " (" + configuration.getDocletSpecificBuildDate() + ") on " + today;
   }
   return new Comment(text);
 }
示例#8
0
  /**
   * Converts a Date in default time zone to client's time zone.
   *
   * @param inputDate a date in the default time zone
   * @return a date in the client time zone
   */
  private Date convertDateToClientTimeZone(Date inputDate) {
    Calendar c = Calendar.getInstance(locale);
    c.setTime(inputDate);

    TimeZone clientZone = timeZoneTracker.getClientTimeZone();
    TimeZone defaultZone = TimeZone.getDefault();
    long now = System.currentTimeMillis();
    int offset = defaultZone.getOffset(now) - clientZone.getOffset(now);

    c.add(Calendar.MILLISECOND, offset);

    return c.getTime();
  }
  public static void main(String[] args) throws Exception {
    JFrame f = new JFrame();

    CalModel cm = new CalModel(TimeZone.getDefault(), true);

    JCalendar jc = new JCalendarDateHHMM();
    jc.setModel(cm);
    f.getContentPane().add((javax.swing.JPanel) jc);

    f.setSize(400, 400);
    f.pack();
    f.setVisible(true);
  }
  void test2() {
    Locale defaultLocale = Locale.getDefault();
    TimeZone reservedTimeZone = TimeZone.getDefault();
    Date d = new Date(2005 - 1900, Calendar.DECEMBER, 22);
    String formatted;

    TimeZone tz;
    SimpleDateFormat df;

    try {
      for (int i = 0; i < TIMEZONES.length; i++) {
        tz = TimeZone.getTimeZone(TIMEZONES[i]);
        TimeZone.setDefault(tz);
        df = new SimpleDateFormat(pattern, DateFormatSymbols.getInstance(OSAKA));
        Locale.setDefault(defaultLocale);
        System.out.println(formatted = df.format(d));
        if (!formatted.equals(DISPLAY_NAMES_OSAKA[i])) {
          throw new RuntimeException(
              "TimeZone "
                  + TIMEZONES[i]
                  + ": formatted zone names mismatch. "
                  + formatted
                  + " should match with "
                  + DISPLAY_NAMES_OSAKA[i]);
        }

        df.parse(DISPLAY_NAMES_OSAKA[i]);

        Locale.setDefault(KYOTO);
        df = new SimpleDateFormat(pattern, DateFormatSymbols.getInstance());
        System.out.println(formatted = df.format(d));
        if (!formatted.equals(DISPLAY_NAMES_KYOTO[i])) {
          throw new RuntimeException(
              "Timezone "
                  + TIMEZONES[i]
                  + ": formatted zone names mismatch. "
                  + formatted
                  + " should match with "
                  + DISPLAY_NAMES_KYOTO[i]);
        }
        df.parse(DISPLAY_NAMES_KYOTO[i]);
      }
    } catch (ParseException pe) {
      throw new RuntimeException("parse error occured" + pe);
    } finally {
      // restore the reserved locale and time zone
      Locale.setDefault(defaultLocale);
      TimeZone.setDefault(reservedTimeZone);
    }
  }
示例#11
0
  public boolean isOlder(Date receivedDate, Date lastReceivedDate) {

    // now we check to see if the timestamp of this message is newer than that of
    // the last received date, so we are getting new google voice messages only
    Calendar newMsgReceivedDate = Calendar.getInstance(TimeZone.getDefault());
    Calendar oldReceivedDate = Calendar.getInstance(TimeZone.getDefault());
    newMsgReceivedDate.setTime(receivedDate);
    oldReceivedDate.setTime(lastReceivedDate);
    if (log.isDebugEnabled())
      log.debug(
          String.format(
              "Calendar Message age check.  Sent date %s %s, received date %s %s",
              newMsgReceivedDate.getTime().getTime(),
              newMsgReceivedDate.getTime(),
              oldReceivedDate.getTime().getTime(),
              oldReceivedDate.getTime()));

    if (newMsgReceivedDate.getTime().compareTo(oldReceivedDate.getTime()) <= 0) return true;

    if (log.isDebugEnabled())
      log.debug(
          String.format(
              "Calendar explicit equal check:  Seconds %s %s \n, minutes %s %s \n, hours %s %s",
              newMsgReceivedDate.get(Calendar.SECOND),
              oldReceivedDate.get(Calendar.SECOND),
              newMsgReceivedDate.get(Calendar.MINUTE),
              oldReceivedDate.get(Calendar.MINUTE),
              newMsgReceivedDate.get(Calendar.HOUR),
              oldReceivedDate.get(Calendar.HOUR)));

    // need to an explicit check if two dates are equal
    return newMsgReceivedDate.get(Calendar.SECOND) == oldReceivedDate.get(Calendar.SECOND)
        && newMsgReceivedDate.get(Calendar.MINUTE) == oldReceivedDate.get(Calendar.MINUTE)
        && newMsgReceivedDate.get(Calendar.HOUR) == oldReceivedDate.get(Calendar.HOUR)
        && newMsgReceivedDate.get(Calendar.DAY_OF_YEAR) == oldReceivedDate.get(Calendar.DAY_OF_YEAR)
        && newMsgReceivedDate.get(Calendar.YEAR) == oldReceivedDate.get(Calendar.YEAR);
  }
示例#12
0
  /** Creates a new instance of Config */
  private Config() {
    getPhoneManufacturer();

    int gmtloc = TimeZone.getDefault().getRawOffset() / 3600000;
    locOffset = 0;
    gmtOffset = gmtloc;

    short greenKeyCode = -1000;

    if (phoneManufacturer == SONYE) {
      // prefetch images
      RosterIcons.getInstance();
      SmilesIcons.getInstance();

      allowMinimize = true;
      greenKeyCode = VirtualList.SE_GREEN;
      if (phoneManufacturer == SONYE_M600) {
        KEY_BACK = -11;
      }
    } else if (phoneManufacturer == NOKIA) {
      // blFlash=false;
      greenKeyCode = VirtualList.NOKIA_GREEN;
    } else if (phoneManufacturer == MOTOEZX) {
      // VirtualList.keyClear=0x1000;
      VirtualList.keyVolDown = VirtualList.MOTOE680_VOL_DOWN;
      KEY_BACK = VirtualList.MOTOE680_REALPLAYER;
    } else if (phoneManufacturer == MOTO) {
      ghostMotor = true;
      // blFlash=false;
      istreamWaiting = true;
      greenKeyCode = VirtualList.MOTOROLA_GREEN;
      // VirtualList.keyClear=0x1000;
    } else if (phoneManufacturer == SIEMENS || phoneManufacturer == SIEMENS2) {
      keyLock = '#';
      keyVibra = '*';
      allowLightControl = true;
      // blFlash=true;
      KEY_BACK = -4; // keyCode==702
      greenKeyCode = VirtualList.SIEMENS_GREEN;
    } else if (phoneManufacturer == WTK) {
      greenKeyCode = VirtualList.NOKIA_GREEN;
    }

    VirtualList.greenKeyCode = greenKeyCode;
  }
 /**
  * Constructs a new GregorianCalender representing the current time, using the default time zone
  * and the default locale.
  */
 public GregorianCalendar() {
   this(TimeZone.getDefault(), Locale.getDefault());
 }
示例#14
0
 private String getTimeZoneOffsetString() {
   SimpleDateFormat format = new SimpleDateFormat("Z");
   format.setTimeZone(TimeZone.getDefault());
   return format.format(new Date());
 }
示例#15
0
 /**
  * Get the day and date information for today, depending upon user option.
  *
  * @return String Today.
  * @see java.util.Calendar
  * @see java.util.GregorianCalendar
  * @see java.util.TimeZone
  */
 public String today() {
   Calendar calendar = new GregorianCalendar(TimeZone.getDefault());
   return calendar.getTime().toString();
 }
示例#16
0
  public void go(SystemEnvironment sysEnv) throws SDMSException {
    try {
      obj.resolve(sysEnv);
    } catch (final NotFoundException nfe) {

    }
    final Long seId = obj.seId;

    final Vector mappedPath = new Vector(obj.path);
    final String mappedName = (String) mappedPath.remove(mappedPath.size() - 1);

    final SDMSSchedule parent = SDMSScheduleTable.getSchedule(sysEnv, mappedPath);
    final Long parentId = parent.getId(sysEnv);

    if (SDMSScheduleTable.idx_parentId_name.containsKey(
        sysEnv, new SDMSKey(parentId, mappedName))) {
      if (replace) {
        final AlterSchedule as = new AlterSchedule(obj, with, Boolean.FALSE);
        as.setEnv(env);
        as.go(sysEnv);
        result = as.result;
        return;
      }

      throw new DuplicateKeyException(
          new SDMSMessage(
              sysEnv,
              "04207251651",
              "Object with name $1 already exists within $2",
              mappedName,
              SDMSScheduleTable.getObject(sysEnv, parentId).pathString(sysEnv)));
    }

    final Long ivalId;
    final String intervalName = (String) with.get(ParseStr.S_INTERVAL);
    if (intervalName == null) ivalId = null;
    else {
      final SDMSInterval ival =
          SDMSIntervalTable.idx_name_getUnique(sysEnv, IntervalUtil.mapIdName(intervalName, seId));
      ivalId = ival.getId(sysEnv);
    }

    final Long uId = env.uid();
    final SDMSUser u = SDMSUserTable.getObject(sysEnv, uId);
    final Long gId;
    if (!with.containsKey(ParseStr.S_GROUP)) {
      gId = u.getDefaultGId(sysEnv);
    } else {
      final String gName = (String) with.get(ParseStr.S_GROUP);
      gId =
          SDMSGroupTable.idx_name_deleteVersion_getUnique(sysEnv, new SDMSKey(gName, new Long(0)))
              .getId(sysEnv);
      if (!SDMSMemberTable.idx_gId_uId.containsKey(sysEnv, new SDMSKey(gId, uId))
          && !SDMSMemberTable.idx_gId_uId.containsKey(
              sysEnv, new SDMSKey(SDMSObject.adminGId, uId))) {
        throw new CommonErrorException(
            new SDMSMessage(
                sysEnv,
                "03401151027",
                "User $1 does not belong to Group $2",
                u.getName(sysEnv),
                gName));
      }
    }

    Long inheritPrivs;
    if (with.containsKey(ParseStr.S_INHERIT)) {
      inheritPrivs = (Long) with.get(ParseStr.S_INHERIT);
      if (inheritPrivs == null) inheritPrivs = new Long(0);
    } else inheritPrivs = null;

    long lpriv = (inheritPrivs == null ? parent.getPrivilegeMask() : inheritPrivs.longValue());
    if ((parent.getPrivilegeMask() & lpriv) != lpriv) {
      throw new CommonErrorException(new SDMSMessage(sysEnv, "03202061327", "Incompatible grant"));
    }

    inheritPrivs = new Long(lpriv);

    if (!with.containsKey(ParseStr.S_ACTIVE)) active = Boolean.TRUE;
    else active = (Boolean) with.get(ParseStr.S_ACTIVE);

    String tz;
    String warning = "";
    if (with.containsKey(ParseStr.S_TIME)) {
      tz = (String) with.get(ParseStr.S_TIME);
      TimeZone tmp = TimeZone.getTimeZone(tz);
      if (!tz.equals(tmp.getID())) {
        throw new CommonErrorException(
            new SDMSMessage(sysEnv, "03207031503", "Time Zone " + tz + " unknown"));
      }
    } else {
      TimeZone tmp = TimeZone.getDefault();
      tz = tmp.getID();
    }

    SDMSScheduleTable.table.create(
        sysEnv, mappedName, gId, ivalId, parentId, tz, seId, active, inheritPrivs);

    result.setFeedback(new SDMSMessage(sysEnv, "04207251652", "Schedule created"));
  }
示例#17
0
/**
 * This is the container for an instance of a site on a single server. This can be access via
 * __instance__
 *
 * @anonymous name : {local}, isField : {true}, desc : {Refers to the site being run.}, type:
 *     {library}
 * @anonymous name : {core}, isField : {true}, desc : {Refers to corejs.} example :
 *     {core.core.mail() calls corejs/core/mail.js}, type : {library}
 * @anonymous name : {external} isField : {true}, desc : {Refers to the external libraries.}, type :
 *     {library}
 * @anonymous name : {db}, isField : {true}, desc : {Refers to the database.}, type : {database}
 * @anonymous name : {setDB} desc : {changes <tt>db</tt> to refer to a different database.} param :
 *     {type : (string) name : (dbname) desc : (name of the database to which to connect)}
 * @anonymous name : {SYSOUT} desc : {Prints a string.} param : {type : (string) name : (str) desc :
 *     (the string to print)}
 * @anonymous name : {log} desc : {Global logger.} param : {type : (string) name : (str) desc : (the
 *     string to log)}
 * @expose
 * @docmodule system.system.__instance__
 */
public class AppContext extends ServletContextBase implements JSObject, Sizable {

  /** @unexpose */
  static final boolean DEBUG = AppServer.D;
  /**
   * If these files exist in the directory or parent directories of a file being run, run these
   * files first. Includes _init.js and /~~/core/init.js.
   */
  static final String INIT_FILES[] = new String[] {"/~~/core/init.js", "PREFIX_init"};

  /**
   * Initializes a new context for a given site directory.
   *
   * @param f the file to run
   */
  public AppContext(File f) {
    this(f.toString());
  }

  /**
   * Initializes a new context for a given site's path.
   *
   * @param root the path to the site from where ed is being run
   */
  public AppContext(String root) {
    this(root, guessNameAndEnv(root).name, guessNameAndEnv(root).env);
  }

  /**
   * Initializes a new context.
   *
   * @param root the path to the site
   * @param name the name of the site
   * @param environment the version of the site
   */
  public AppContext(String root, String name, String environment) {
    this(root, new File(root), name, environment);
  }

  /**
   * Initializes a new context.
   *
   * @param root the path to the site
   * @param rootFile the directory in which the site resides
   * @param name the name of the site
   * @param environment the version of the site
   */
  public AppContext(String root, File rootFile, String name, String environment) {
    this(root, rootFile, name, environment, null);
  }

  private AppContext(
      String root, File rootFile, String name, String environment, AppContext nonAdminParent) {
    super(name + ":" + environment);
    if (root == null) throw new NullPointerException("AppContext root can't be null");

    if (rootFile == null) throw new NullPointerException("AppContext rootFile can't be null");

    if (name == null) name = guessNameAndEnv(root).name;

    if (name == null) throw new NullPointerException("how could name be null");

    _root = root;
    _rootFile = rootFile;
    _git = new GitDir(_rootFile);
    _name = name;

    _environment = environment;
    _nonAdminParent = nonAdminParent;
    _admin = _nonAdminParent != null;
    _codePrefix = _admin ? "/~~/modules/admin/" : "";
    _moduleRegistry = ModuleRegistry.getNewGlobalChild();

    if (_git.isValid()) {
      _gitBranch = _git.getBranchOrTagName();
      _gitHash = _git.getCurrentHash();
    }

    _isGrid = name.equals("grid");

    _scope =
        new Scope(
            "AppContext:" + root + (_admin ? ":admin" : ""),
            _isGrid ? ed.cloud.Cloud.getInstance().getScope() : Scope.newGlobal(),
            null,
            Language.JS(),
            _rootFile);
    _scope.setGlobal(true);
    _initScope = _scope.child("_init");

    _usage = new UsageTracker(this);

    _baseScopeInit();

    _adminContext = _admin ? null : new AppContext(root, rootFile, name, environment, this);

    _rootContextReachable = new SeenPath();

    if (!_admin)
      _logger.info(
          "Started Context.  root:"
              + _root
              + " environment:"
              + environment
              + " git branch: "
              + _gitBranch);
  }

  /**
   * Returns the adapter type for the given file. Will first use the adapter selector function if it
   * was specified in init.js, otherwise will use the static type (either set in _init file, as a
   * server-wide override in 10gen.properties, or default of DIRECT_10GEN)
   *
   * @param file to produce type for
   * @return adapter type for the specified file
   */
  public AdapterType getAdapterType(File file) {

    // Q : I think this is the right thing to do
    if (inScopeSetup()) {
      return AdapterType.DIRECT_10GEN;
    }

    /*
     * cheap hack - prevent any _init.* file from getting run as anythign but DIRECT_10GEN
     */

    if (file != null && file.getName().indexOf("_init.") != -1) {
      return AdapterType.DIRECT_10GEN;
    }

    if (_adapterSelector == null) {
      return _staticAdapterType;
    }

    /*
     *  only let the app select type if file is part of application (i.e.
     *  don't do it for corejs, core modules, etc...
     */

    String fp = file.getAbsolutePath();
    String fullRoot = _rootFile.getAbsolutePath(); // there must be a nicer way to do this?

    if (!fp.startsWith(fullRoot)) {
      return AdapterType.DIRECT_10GEN;
    }

    Object o = _adapterSelector.call(_initScope, new JSString(fp.substring(fullRoot.length())));

    if (o == null) {
      return _staticAdapterType;
    }

    if (!(o instanceof JSString)) {
      log("Error : adapter selector not returning string.  Ignoring and using static adapter type");
      return _staticAdapterType;
    }

    AdapterType t = getAdapterTypeFromString(o.toString());

    return (t == null ? _staticAdapterType : t);
  }

  /**
   * Creates a copy of this context.
   *
   * @return an identical context
   */
  AppContext newCopy() {
    return new AppContext(_root, _rootFile, _name, _environment, _nonAdminParent);
  }

  /** Initializes the base scope for the application */
  private void _baseScopeInit() {
    // --- libraries

    if (_admin) _scope.put("local", new JSObjectBase(), true);
    else _setLocalObject(new JSFileLibrary(_rootFile, "local", this));

    _loadConfig();

    _core = CoreJS.get().getLibrary(getCoreJSVersion(), this, null, true);
    _logger.info("corejs : " + _core.getRoot());
    _scope.put("core", _core, true);

    _external =
        Module.getModule("external").getLibrary(getVersionForLibrary("external"), this, null, true);
    _scope.put("external", _external, true);

    _scope.put("__instance__", this, true);
    _scope.lock("__instance__");

    // --- db

    if (!_isGrid) {
      _scope.put("db", DBProvider.get(this), true);
      _scope.put(
          "setDB",
          new JSFunctionCalls1() {

            public Object call(Scope s, Object name, Object extra[]) {
              if (name.equals(_lastSetTo)) return true;

              DBBase db = (DBBase) AppContext.this._scope.get("db");
              if (!db.allowedToAccess(name.toString()))
                throw new JSException("you are not allowed to access db [" + name + "]");

              if (name.equals(db.getName())) return true;

              AppContext.this._scope.put(
                  "db", DBProvider.get(AppContext.this, name.toString()), false);
              _lastSetTo = name.toString();

              if (_adminContext != null) {
                // yes, i do want a new copy so Constructors don't get copied for both
                _adminContext._scope.put(
                    "db", DBProvider.get(AppContext.this, name.toString()), false);
              }

              return true;
            }

            String _lastSetTo = null;
          },
          true);
    }

    // --- output

    _scope.put(
        "SYSOUT",
        new JSFunctionCalls1() {
          public Object call(Scope s, Object str, Object foo[]) {
            System.out.println(AppContext.this._name + " \t " + str);
            return true;
          }
        },
        true);

    _scope.put("log", _logger, true);

    // --- random?

    _scope.put(
        "openFile",
        new JSFunctionCalls1() {
          public Object call(Scope s, Object name, Object extra[]) {
            return new JSLocalFile(_rootFile, name.toString());
          }
        },
        true);

    _scope.put("globalHead", _globalHead, true);

    Map<String, JSFileLibrary> rootFileMap = new HashMap<String, JSFileLibrary>();
    for (String rootKey : new String[] {"local", "core", "external"}) {
      Object temp = _scope.get(rootKey);
      if (temp instanceof JSFileLibrary) rootFileMap.put(rootKey, (JSFileLibrary) temp);
    }

    _scope.put(
        "fork",
        new JSFunctionCalls1() {
          public Object call(final Scope scope, final Object funcJS, final Object extra[]) {

            if (!(funcJS instanceof JSFunction))
              throw new JSException("fork has to take a function");

            return queueWork("forked", (JSFunction) funcJS, extra);
          }
        });
    _scope.lock("fork");

    ed.appserver.templates.djang10.JSHelper.install(_scope, rootFileMap, _logger);

    _scope.lock("user"); // protection against global user object
  }

  private void _loadConfig() {
    try {

      _configScope.set("__instance__", this);

      _loadConfigFromCloudObject(getSiteObject());
      _loadConfigFromCloudObject(getEnvironmentObject());

      File f;
      if (!_admin) {
        f = getFileSafe("_config.js");
        if (f == null || !f.exists()) f = getFileSafe("_config");
      } else
        f =
            new File(
                Module.getModule("core-modules/admin").getRootFile(getVersionForLibrary("admin")),
                "_config.js");

      _libraryLogger.info("config file [" + f + "] exists:" + f.exists());

      if (f == null || !f.exists()) return;

      Convert c = new Convert(f);
      JSFunction func = c.get();
      func.setUsePassedInScope(true);
      func.call(_configScope);

      _logger.debug("config things " + _configScope.keySet());
    } catch (Exception e) {
      throw new RuntimeException("couldn't load config", e);
    }
  }

  private void _loadConfigFromCloudObject(JSObject o) {
    if (o == null) return;

    _configScope.putAll((JSObject) o.get("config"));
  }

  /**
   * Get the version of corejs to run for this AppContext.
   *
   * @return the version of corejs as a string. null if should use default
   */
  public String getCoreJSVersion() {
    Object o = _scope.get("corejsversion");
    if (o != null) {
      _logger.error("you are using corejsversion which is deprecated.  please use version.corejs");
      return JS.toString(o);
    }

    return getVersionForLibrary("corejs");
  }

  /**
   * Get the version of a library to run.
   *
   * @param name the name of the library to look up
   * @return the version of the library to run as a string. null if should use default
   */
  public String getVersionForLibrary(String name) {
    String version = getVersionForLibrary(_configScope, name, this);
    _libraryVersions.set(name, version);
    return version;
  }

  public JSObject getLibraryVersionsLoaded() {
    return _libraryVersions;
  }

  /** @unexpose */
  public static String getVersionForLibrary(Scope s, String name) {
    AppRequest ar = AppRequest.getThreadLocal();
    return getVersionForLibrary(s, name, ar == null ? null : ar.getContext());
  }

  /** @unexpose */
  private static String getVersionForLibrary(Scope s, String name, AppContext ctxt) {
    final String version = _getVersionForLibrary(s, name, ctxt);
    _libraryLogger.log(
        ctxt != null && !ctxt._admin ? Level.DEBUG : Level.INFO,
        ctxt + "\t" + name + "\t" + version);
    return version;
  }

  private static String _getVersionForLibrary(Scope s, String name, AppContext ctxt) {
    final JSObject o1 =
        ctxt == null ? null : (JSObject) (s.get("version_" + ctxt.getEnvironmentName()));
    final JSObject o2 = (JSObject) s.get("version");

    _libraryLogger.debug(ctxt + "\t versionConfig:" + (o1 != null) + " config:" + (o2 != null));

    String version = _getString(name, o1, o2);
    if (version != null) return version;

    if (ctxt == null || ctxt._nonAdminParent == null) return null;

    return ctxt._nonAdminParent.getVersionForLibrary(name);
  }

  private static String _getString(String name, JSObject... places) {
    for (JSObject o : places) {
      if (o == null) continue;
      Object temp = o.get(name);
      if (temp == null) continue;
      return temp.toString();
    }
    return null;
  }

  /** @return [ <name> , <env> ] */
  static NameAndEnv guessNameAndEnv(String root) {
    root = ed.io.FileUtil.clean(root);
    root = root.replaceAll("\\.+/", "");
    String pcs[] = root.split("/+");

    if (pcs.length == 0) throw new RuntimeException("no root for : " + root);

    // handle anything with sites/foo
    for (int i = 0; i < pcs.length - 1; i++)
      if (pcs[i].equals("sites")) {
        return new NameAndEnv(pcs[i + 1], i + 2 < pcs.length ? pcs[i + 2] : null);
      }

    final int start = pcs.length - 1;
    for (int i = start; i > 0; i--) {
      String s = pcs[i];

      if (i == start
          && (s.equals("master")
              || s.equals("test")
              || s.equals("www")
              || s.equals("staging")
              ||
              // s.equals("stage") ||
              s.equals("dev"))) continue;

      return new NameAndEnv(s, i + 1 < pcs.length ? pcs[i + 1] : null);
    }

    return new NameAndEnv(pcs[0], pcs.length > 1 ? pcs[1] : null);
  }

  static class NameAndEnv {
    NameAndEnv(String name, String env) {
      this.name = name;
      this.env = env;
    }

    final String name;
    final String env;
  }

  /**
   * Returns the name of the site being run.
   *
   * @return the name of the site
   */
  public String getName() {
    return _name;
  }

  /**
   * Get the database being used.
   *
   * @return The database being used
   */
  public DBBase getDB() {
    return (DBBase) _scope.get("db");
  }

  /**
   * Given the _id of a JSFile, return the file.
   *
   * @param id _id of the file to find
   * @return The file, if found, otherwise null
   */
  JSFile getJSFile(String id) {

    if (id == null) return null;

    DBCollection f = getDB().getCollection("_files");
    return (JSFile) (f.find(new ObjectId(id)));
  }

  /**
   * Returns (and if necessary, reinitializes) the scope this context is using.
   *
   * @return the scope
   */
  public Scope getScope() {
    return _scope();
  }

  public Scope getInitScope() {
    return _initScope;
  }

  public Object getInitObject(String what) {
    return getFromInitScope(what);
  }

  public Object getFromInitScope(String what) {
    if (!_knownInitScopeThings.contains(what))
      System.err.println("*** Unknown thing requested from initScope [" + what + "]");
    return _initScope.get(what);
  }

  public void setInitObject(String name, Object value) {
    _initScope.set(name, value);
  }

  void setTLPreferredScope(AppRequest req, Scope s) {
    _scope.setTLPreferred(s);
  }

  private synchronized Scope _scope() {

    if (_inScopeSetup) return _scope;

    if (_getScopeTime() > _lastScopeInitTime) _scopeInited = false;

    if (_scopeInited) return _scope;

    _scopeInited = true;
    _lastScopeInitTime = System.currentTimeMillis();

    _setupScope();

    _setStaticAdapterType();

    _setAdapterSelectorFunction();

    return _scope;
  }

  protected void _setAdapterSelectorFunction() {

    Object o = this.getFromInitScope(INIT_ADAPTER_SELECTOR);

    if (o == null) {
      log("Adapter selector function not specified in _init file");
      return;
    }

    if (!(o instanceof JSFunction)) {
      log(
          "Adapter selector function specified in _init file  not a function.  Ignoring. ["
              + o.getClass()
              + "]");
      return;
    }

    _adapterSelector = (JSFunction) o;
    log("Adapter selector function specified in _init file");
  }

  public void setStaticAdapterTypeValue(AdapterType type) {
    log("Static adapter type directly set : " + type);
    _staticAdapterType = type;
  }

  public AdapterType getStaticAdapterTypeValue() {
    return _staticAdapterType;
  }

  /**
   * Figure out what kind of static adapter type was specified. By default it's a 10genDEFAULT app
   */
  protected void _setStaticAdapterType() {

    /*
     *  app configuration steps could have set this already.  If so, don't bother doing anything
     */
    if (_staticAdapterType != AdapterType.UNSET) {
      log("Static adapter type has already been directly set to " + _staticAdapterType);
      return;
    }

    /*
     * check to see if overridden in 10gen.properties
     */
    String override = Config.get().getProperty(INIT_ADAPTER_TYPE);

    if (override != null) {
      AdapterType t = getAdapterTypeFromString(override);

      if (t == null) {
        log(
            "Static adapter type specified as override ["
                + override
                + "] unknown - will use _init file specified or default");
      } else {
        log("Static adapter type overridden by 10gen.properties or env. Value : " + override);
        _staticAdapterType = t;
        return;
      }
    }

    /*
     *  if not, use the one from _init file if specified
     */

    _staticAdapterType = AdapterType.DIRECT_10GEN;
    Object o = getFromInitScope(INIT_ADAPTER_TYPE);

    if (o == null) {
      log("Static adapter type not specified in _init file - using default value of DIRECT_10GEN");
      return;
    }

    if (!(o instanceof JSString)) {
      log("Static adapter type from _init file not a string - using default value of DIRECT_10GEN");
      return;
    }

    _staticAdapterType = getAdapterTypeFromString(o.toString());

    if (_staticAdapterType == null) {
      log(
          "Static adapter type from _init file ["
              + o.toString()
              + "] unknown - using default value of DIRECT_10GEN");
      _staticAdapterType = AdapterType.DIRECT_10GEN;
      return;
    }

    log("Static adapter type specified in _init file = " + _staticAdapterType);

    return;
  }

  public AdapterType getAdapterTypeFromString(String s) {

    if (AdapterType.DIRECT_10GEN.toString().equals(s.toUpperCase())) {
      return AdapterType.DIRECT_10GEN;
    }

    if (AdapterType.CGI.toString().equals(s.toUpperCase())) {
      return AdapterType.CGI;
    }

    if (AdapterType.WSGI.toString().equals(s.toUpperCase())) {
      return AdapterType.WSGI;
    }

    return null;
  }

  /** @unexpose */
  public File getFileSafe(final String uri) {
    try {
      return getFile(uri);
    } catch (FileNotFoundException fnf) {
      return null;
    }
  }

  /** @unexpose */
  public File getFile(final String uri) throws FileNotFoundException {
    File f = _files.get(uri);

    if (f != null) return f;

    if (uri.startsWith("/~~/") || uri.startsWith("~~/"))
      f = _core.getFileFromPath(uri.substring(3));
    else if (uri.startsWith("/admin/")) f = _core.getFileFromPath("/modules" + uri);
    else if (uri.startsWith("/@@/") || uri.startsWith("@@/"))
      f = _external.getFileFromPath(uri.substring(3));
    else if (_localObject != null && uri.startsWith("/modules/"))
      f = _localObject.getFileFromPath(uri);
    else f = new File(_rootFile, uri);

    if (f == null) throw new FileNotFoundException(uri);

    _files.put(uri, f);
    return f;
  }

  public String getRealPath(String path) {
    try {
      return getFile(path).getAbsolutePath();
    } catch (FileNotFoundException fnf) {
      throw new RuntimeException("file not found [" + path + "]");
    }
  }

  public URL getResource(String path) {
    try {
      File f = getFile(path);
      if (!f.exists()) return null;
      return f.toURL();
    } catch (FileNotFoundException fnf) {
      // the spec says to return null if we can't find it
      // even though this is weird...
      return null;
    } catch (IOException ioe) {
      throw new RuntimeException("error opening [" + path + "]", ioe);
    }
  }

  public InputStream getResourceAsStream(String path) {
    URL url = getResource(path);
    if (url == null) return null;
    try {
      return url.openStream();
    } catch (IOException ioe) {
      throw new RuntimeException("can't getResourceAsStream [" + path + "]", ioe);
    }
  }

  /**
   * This causes the AppContext to be started over. All context level variable will be lost. If code
   * is being managed, will cause it to check that its up to date.
   */
  public void reset() {
    _reset = true;
  }

  /** Checks if this context has been reset. */
  public boolean isReset() {
    return _reset;
  }

  /**
   * Returns the path to the directory the appserver is running. (For example, site/version.)
   *
   * @return the path
   */
  public String getRoot() {
    return _root;
  }

  public File getRootFile() {
    return _rootFile;
  }

  /**
   * Creates an new request for the app server from an HTTP request.
   *
   * @param request HTTP request to create
   * @return the request
   */
  public AppRequest createRequest(HttpRequest request) {
    return createRequest(request, request.getHost(), request.getURI());
  }

  /**
   * Creates an new request for the app server from an HTTP request.
   *
   * @param request HTTP request to create
   * @param uri the URI requested
   * @return the request
   */
  public AppRequest createRequest(HttpRequest request, String host, String uri) {
    _numRequests++;

    if (AppRequest.isAdmin(request)) return new AppRequest(_adminContext, request, host, uri);

    return new AppRequest(this, request, host, uri);
  }

  /**
   * Tries to find the given file, assuming that it's missing the ".jxp" extension
   *
   * @param f File to check
   * @return same file if not found to be missing the .jxp, or a new File w/ the .jxp appended
   */
  File tryNoJXP(File f) {
    if (f.exists()) return f;

    if (f.getName().indexOf(".") >= 0) return f;

    File temp = new File(f.toString() + ".jxp");
    return temp.exists() ? temp : f;
  }

  File tryOtherExtensions(File f) {
    if (f.exists()) return f;

    if (f.getName().indexOf(".") >= 0) return f;

    for (int i = 0; i < JSFileLibrary._srcExtensions.length; i++) {
      File temp = new File(f.toString() + JSFileLibrary._srcExtensions[i]);
      if (temp.exists()) return temp;
    }

    return f;
  }

  /**
   * Maps a servlet-like URI to a jxp file.
   *
   * @param f File to check
   * @return new File with <root>.jxp if exists, orig file if not
   * @example /wiki/geir -> maps to wiki.jxp if exists
   */
  File tryServlet(File f) {
    if (f.exists()) return f;

    String uri = f.toString();

    if (uri.startsWith(_rootFile.toString())) uri = uri.substring(_rootFile.toString().length());

    if (_core != null && uri.startsWith(_core._base.toString()))
      uri = "/~~" + uri.substring(_core._base.toString().length());

    while (uri.startsWith("/")) uri = uri.substring(1);

    int start = 0;
    while (true) {

      int idx = uri.indexOf("/", start);
      if (idx < 0) break;
      String foo = uri.substring(0, idx);

      File temp = getFileSafe(foo + ".jxp");

      if (temp != null && temp.exists()) f = temp;

      start = idx + 1;
    }

    return f;
  }

  /**
   * Returns the index.jxp for the File argument if it's an existing directory, and the index.jxp
   * file exists
   *
   * @param f directory to check
   * @return new File for index.jxp in that directory, or same file object if not
   */
  File tryIndex(File f) {

    if (!(f.isDirectory() && f.exists())) return f;

    for (int i = 0; i < JSFileLibrary._srcExtensions.length; i++) {
      File temp = new File(f, "index" + JSFileLibrary._srcExtensions[i]);
      if (temp.exists()) return temp;
    }

    return f;
  }

  JxpSource getSource(File f) throws IOException {

    if (DEBUG) System.err.println("getSource\n\t " + f);

    File temp = _findFile(f);

    if (DEBUG) System.err.println("\t " + temp);

    if (!temp.exists()) return handleFileNotFound(f);

    //  if it's a directory (and we know we can't find the index file)
    //  TODO : at some point, do something where we return an index for the dir?
    if (temp.isDirectory()) return null;

    // if we are at init time, save it as an initializaiton file
    loadedFile(temp);

    // Ensure that this is w/in the right tree for the context
    if (_localObject != null && _localObject.isIn(temp)) return _localObject.getSource(temp);

    // if not, is it core?
    if (_core.isIn(temp)) return _core.getSource(temp);

    throw new RuntimeException("what?  can't find:" + f);
  }

  /**
   * Finds the appropriate file for the given path.
   *
   * <p>We have a hierarchy of attempts as we try to find a file :
   *
   * <p>1) first, see if it exists as is, or if it's really a .jxp w/o the extension 2) next, see if
   * it can be deconstructed as a servlet such that /foo/bar maps to /foo.jxp 3) See if we can find
   * the index file for it if a directory
   */
  File _findFile(File f) {

    File temp;

    if ((temp = tryNoJXP(f)) != f) {
      return temp;
    }

    if ((temp = tryOtherExtensions(f)) != f) {
      return temp;
    }

    if ((temp = tryServlet(f)) != f) {
      return temp;
    }

    if ((temp = tryIndex(f)) != f) {
      return temp;
    }

    return f;
  }

  public void loadedFile(File f) {
    if (_inScopeSetup) _initFlies.add(f);
  }

  public void addInitDependency(File f) {
    _initFlies.add(f);
  }

  JxpServlet getServlet(File f) throws IOException {
    // if this site doesn't exist, don't return anything
    if (!_rootFile.exists()) return null;

    JxpSource source = getSource(f);
    if (source == null) return null;
    return source.getServlet(this);
  }

  private void _setupScope() {
    if (_inScopeSetup) return;

    final Scope saveTLPref = _scope.getTLPreferred();
    _scope.setTLPreferred(null);

    final Scope saveTL = Scope.getThreadLocal();
    _scope.makeThreadLocal();

    _inScopeSetup = true;

    try {
      Object fo = getConfigObject("framework");

      if (fo != null) {

        Framework f = null;

        if (fo instanceof JSString) {
          f = Framework.byName(fo.toString(), null); // we allow people to just specify name
          _logger.info("Setting framework by name [" + fo.toString() + "]");
        } else if (fo instanceof JSObjectBase) {

          JSObjectBase obj = (JSObjectBase) fo;

          if (obj.containsKey("name")) {

            String s = obj.getAsString("version");

            f = Framework.byName(obj.getAsString("name"), s);
            _logger.info("Setting framework by name [" + obj.getAsString("name") + "]");
          } else if (obj.containsKey("custom")) {

            Object o = obj.get("custom");

            if (o instanceof JSObjectBase) {
              f = Framework.byCustom((JSObjectBase) o);
              _logger.info("Setting framework by custom [" + o + "]");
            } else {
              throw new RuntimeException("Error - custom framework wasn't an object [" + o + "]");
            }
          } else if (obj.containsKey("classname")) {
            f = Framework.byClass(obj.getAsString("classname"));
            _logger.info("Setting framework by class [" + obj.getAsString("classname") + "]");
          }
        }

        if (f == null) {
          throw new RuntimeException("Error : can't find framework [" + fo + "]");
        }

        f.install(this);
      }

      _runInitFiles(INIT_FILES);

      if (_adminContext != null) {
        _adminContext._scope.set("siteScope", _scope);
        _adminContext._setLocalObject(_localObject);
      }

      _lastScopeInitTime = _getScopeTime();
    } catch (RuntimeException re) {
      _scopeInited = false;
      throw re;
    } catch (Exception e) {
      _scopeInited = false;
      throw new RuntimeException(e);
    } finally {
      _inScopeSetup = false;
      _scope.setTLPreferred(saveTLPref);

      if (saveTL != null) saveTL.makeThreadLocal();

      this.approxSize(_rootContextReachable);
    }
  }

  public boolean inScopeSetup() {
    return _inScopeSetup;
  }

  private void _runInitFiles(String[] files) throws IOException {

    if (files == null) return;

    for (JSFunction func : _initRefreshHooks) {
      func.call(_initScope, null);
    }

    for (int i = 0; i < files.length; i++) runInitFile(files[i].replaceAll("PREFIX", _codePrefix));
  }

  public void addInitRefreshHook(JSFunction func) {
    _initRefreshHooks.add(func);
  }

  /** @param path (ex: /~~/foo.js ) */
  public void runInitFile(String path) throws IOException {
    _runInitFile(tryOtherExtensions(getFile(path)));
  }

  private void _runInitFile(File f) throws IOException {
    if (f == null) return;

    if (!f.exists()) return;

    _initFlies.add(f);
    JxpSource s = getSource(f);
    JSFunction func = s.getFunction();
    func.setUsePassedInScope(true);
    func.call(_initScope);
  }

  long _getScopeTime() {
    long last = 0;
    for (File f : _initFlies) if (f.exists()) last = Math.max(last, f.lastModified());
    return last;
  }

  /**
   * Convert this AppContext to a string by returning the name of the directory it's running in.
   *
   * @return the filename of its root directory
   */
  public String toString() {
    return _rootFile.toString();
  }

  public String debugInfo() {
    return _rootFile + " admin:" + _admin;
  }

  public void fix(Throwable t) {
    StackTraceHolder.getInstance().fix(t);
  }

  /**
   * Get a "global" head array. This array contains HTML that will be inserted into the head of
   * every request served by this app context. It's analagous to the <tt>head</tt> array, but
   * persistent.
   *
   * @return a mutable array
   */
  public JSArray getGlobalHead() {
    return _globalHead;
  }

  /**
   * Gets the date of creation for this app context.
   *
   * @return the creation date as a JS Date.
   */
  public JSDate getWhenCreated() {
    return _created;
  }

  /**
   * Gets the number of requests served by this app context.
   *
   * @return the number of requests served
   */
  public int getNumRequests() {
    return _numRequests;
  }

  /**
   * Get the name of the git branch we think we're running.
   *
   * @return the name of the git branch, as a string
   */
  public String getGitBranch() {
    return _gitBranch;
  }

  public String getGitHash() {
    return _gitHash;
  }

  /**
   * Update the git branch that we're running and return it.
   *
   * @return the name of the git branch, or null if there isn't any
   */
  public String getCurrentGitBranch() {
    return getCurrentGitBranch(false);
  }

  public String getCurrentGitBranch(boolean forceUpdate) {
    if (_gitBranch == null) return null;

    if (_gitFile == null) _gitFile = new File(_rootFile, ".git/HEAD");

    if (!_gitFile.exists()) throw new RuntimeException("this should be impossible");

    if (forceUpdate || _lastScopeInitTime < _gitFile.lastModified()) {
      _gitBranch = _git.getBranchOrTagName();
      _gitHash = _git.getCurrentHash();
    }

    return _gitBranch;
  }

  /**
   * Get the environment in which this site is running
   *
   * @return the environment name as a string
   */
  public String getEnvironmentName() {
    return _environment;
  }

  /**
   * updates the context to the correct branch based on environment and to the latest version of the
   * code if name or environemnt is missing, does nothing
   */
  public String updateCode() {

    if (!_git.isValid()) throw new RuntimeException(_rootFile + " is not a git repository");

    _logger.info("going to update code");
    _git.fullUpdate();

    if (_name == null || _environment == null) return getCurrentGitBranch();

    JSObject env = getEnvironmentObject();
    if (env == null) return null;

    String branch = env.get("branch").toString();
    _logger.info("updating to [" + branch + "]");

    _git.checkout(branch);
    Python.deleteCachedJythonFiles(_rootFile);

    return getCurrentGitBranch(true);
  }

  private JSObject getSiteObject() {
    return AppContextHolder.getSiteFromCloud(_name);
  }

  private JSObject getEnvironmentObject() {
    return AppContextHolder.getEnvironmentFromCloud(_name, _environment);
  }

  private void _setLocalObject(JSFileLibrary local) {
    _localObject = local;
    _scope.put("local", _localObject, true);
    _scope.put("jxp", _localObject, true);
    _scope.warn("jxp");
  }

  JxpSource handleFileNotFound(File f) {
    String name = f.getName();
    if (name.endsWith(".class")) {
      name = name.substring(0, name.length() - 6);
      return getJxpServlet(name);
    }

    return null;
  }

  public JxpSource getJxpServlet(String name) {
    JxpSource source = _httpServlets.get(name);
    if (source != null) return source;

    try {
      Class c = Class.forName(name);
      Object n = c.newInstance();
      if (!(n instanceof HttpServlet))
        throw new RuntimeException("class [" + name + "] is not a HttpServlet");

      HttpServlet servlet = (HttpServlet) n;
      servlet.init(createServletConfig(name));
      source = new ServletSource(servlet);
      _httpServlets.put(name, source);
      return source;
    } catch (Exception e) {
      throw new RuntimeException("can't load [" + name + "]", e);
    }
  }

  ServletConfig createServletConfig(final String name) {
    final Object rawServletConfigs = _scope.get("servletConfigs");
    final Object servletConfigObject =
        rawServletConfigs instanceof JSObject ? ((JSObject) rawServletConfigs).get(name) : null;
    final JSObject servletConfig;
    if (servletConfigObject instanceof JSObject) servletConfig = (JSObject) servletConfigObject;
    else servletConfig = null;

    return new ServletConfig() {
      public String getInitParameter(String name) {
        if (servletConfig == null) return null;
        Object foo = servletConfig.get(name);
        if (foo == null) return null;
        return foo.toString();
      }

      public Enumeration getInitParameterNames() {
        Collection keys;
        if (servletConfig == null) keys = new LinkedList();
        else keys = servletConfig.keySet();
        return new CollectionEnumeration(keys);
      }

      public ServletContext getServletContext() {
        return AppContext.this;
      }

      public String getServletName() {
        return name;
      }
    };
  }

  public static AppContext findThreadLocal() {

    AppContext context = _tl.get();
    if (context != null) return context;

    AppRequest req = AppRequest.getThreadLocal();
    if (req != null) return req._context;

    Scope s = Scope.getThreadLocal();
    if (s != null) {
      Object foo = s.get("__instance__");
      if (foo instanceof AppContext) return (AppContext) foo;
    }

    return null;
  }

  public void makeThreadLocal() {
    _tl.set(this);
  }

  public static void clearThreadLocal() {
    _tl.set(null);
  }

  public String getInitParameter(String name) {
    Object foo = _configScope.get(name);
    if (foo == null) return null;
    return foo.toString();
  }

  public Enumeration getInitParameterNames() {
    return new CollectionEnumeration(_configScope.keySet());
  }

  public Object getConfigObject(String name) {
    return _configScope.get(name);
  }

  public void setConfigObject(String name, Object value) {
    _configScope.set(name, value);
  }

  public String getContextPath() {
    return "";
  }

  public RequestDispatcher getNamedDispatcher(String name) {
    throw new RuntimeException("getNamedDispatcher not implemented");
  }

  public RequestDispatcher getRequestDispatcher(String name) {
    throw new RuntimeException("getRequestDispatcher not implemented");
  }

  public Set getResourcePaths(String path) {
    throw new RuntimeException("getResourcePaths not implemented");
  }

  public AppContext getSiteInstance() {
    if (_nonAdminParent == null) return this;
    return _nonAdminParent;
  }

  public long approxSize() {
    return approxSize(new SeenPath());
  }

  public long approxSize(SeenPath seen) {
    long size = 0;

    seen.visited(this);

    if (seen.shouldVisit(_scope, this)) size += _scope.approxSize(seen, false, true);
    if (seen.shouldVisit(_initScope, this)) size += _initScope.approxSize(seen, true, false);

    size += JSObjectSize.size(_localObject, seen, this);
    size += JSObjectSize.size(_core, seen, this);
    size += JSObjectSize.size(_external, seen, this);

    if (seen.shouldVisit(_adminContext, this)) size += _adminContext.approxSize(seen);

    size += JSObjectSize.size(_logger, seen, this);

    return size;
  }

  public int hashCode() {
    return System.identityHashCode(this);
  }

  public boolean equals(Object o) {
    return o == this;
  }

  public AppWork queueWork(String identifier, JSFunction work, Object... params) {
    return queueWork(new AppWork.FunctionAppWork(this, identifier, work, params));
  }

  public AppWork queueWork(AppWork work) {
    if (_workQueue == null) {
      _workQueue = new ArrayBlockingQueue<AppWork>(100);
      AppWork.addQueue(_workQueue);
    }

    if (_workQueue.offer(work)) return work;

    throw new RuntimeException("work queue full!");
  }

  public Logger getLogger(String sub) {
    return _logger.getChild(sub);
  }

  public ModuleRegistry getModuleRegistry() {
    return _moduleRegistry;
  }

  // ----  START JSObject INTERFACE

  public Object get(Object n) {
    return _scope.get(n);
  }

  public JSFunction getFunction(String name) {
    return _scope.getFunction(name);
  }

  public final Set<String> keySet() {
    return _scope.keySet();
  }

  public Set<String> keySet(boolean includePrototype) {
    return _scope.keySet(includePrototype);
  }

  public boolean containsKey(String s) {
    return _scope.containsKey(s);
  }

  public boolean containsKey(String s, boolean includePrototype) {
    return _scope.containsKey(s, includePrototype);
  }

  public Object set(Object n, Object v) {
    return _scope.putExplicit(n.toString(), v);
  }

  public Object setInt(int n, Object v) {
    throw new RuntimeException("not allowed");
  }

  public Object getInt(int n) {
    return _scope.getInt(n);
  }

  public Object removeField(Object n) {
    return _scope.removeField(n);
  }

  public JSFunction getConstructor() {
    return null;
  }

  public JSObject getSuper() {
    return null;
  }

  // ----  END BROKEN JSOBJET INTERFACE

  public TimeZone getTimeZone() {
    return _tz;
  }

  public void setTimeZone(String tz) {
    if (tz.length() == 3) tz = tz.toUpperCase();
    _tz = TimeZone.getTimeZone(tz);
    if (!_tz.getID().equals(tz)) throw new RuntimeException("can't find time zone[" + tz + "]");
  }

  final String _name;
  final String _root;
  final File _rootFile;
  final GitDir _git;

  private String _gitBranch;
  private String _gitHash;
  final String _environment;
  final boolean _admin;

  final AppContext _adminContext;
  final String _codePrefix;

  final AppContext _nonAdminParent;

  private JSFileLibrary _localObject;
  private JSFileLibrary _core;
  private JSFileLibrary _external;

  final Scope _scope;
  final SeenPath _rootContextReachable;
  final Scope _initScope;
  final Scope _configScope = new Scope();
  final UsageTracker _usage;
  final ModuleRegistry _moduleRegistry;

  final JSArray _globalHead = new JSArray();

  private final Map<String, File> _files = Collections.synchronizedMap(new HashMap<String, File>());
  private final Set<File> _initFlies = new HashSet<File>();
  private final Map<String, JxpSource> _httpServlets =
      Collections.synchronizedMap(new HashMap<String, JxpSource>());
  private final JSObject _libraryVersions = new JSObjectBase();

  private Queue<AppWork> _workQueue;
  private TimeZone _tz = TimeZone.getDefault();

  boolean _scopeInited = false;
  boolean _inScopeSetup = false;
  long _lastScopeInitTime = 0;

  final boolean _isGrid;

  boolean _reset = false;
  int _numRequests = 0;
  final JSDate _created = new JSDate();

  private File _gitFile = null;
  private long _lastGitCheckTime = 0;

  private Collection<JSFunction> _initRefreshHooks = new ArrayList<JSFunction>();

  /*
   *  adapter type - can have either a static ("all files in this app are X")
   *  or dynamic - the provided selector function dynamically chooses, falling
   *  back to the static if it returns null
   */
  public static final String INIT_ADAPTER_TYPE = "adapterType";
  public static final String INIT_ADAPTER_SELECTOR = "adapterSelector";

  private AdapterType _staticAdapterType = AdapterType.UNSET;
  private JSFunction _adapterSelector = null;

  private static Logger _libraryLogger = Logger.getLogger("library.load");

  static {
    _libraryLogger.setLevel(Level.INFO);
  }

  private static final Set<String> _knownInitScopeThings = new HashSet<String>();
  private static final ThreadLocal<AppContext> _tl = new ThreadLocal<AppContext>();

  static {
    _knownInitScopeThings.add("mapUrlToJxpFileCore");
    _knownInitScopeThings.add("mapUrlToJxpFile");
    _knownInitScopeThings.add("allowed");
    _knownInitScopeThings.add("staticCacheTime");
    _knownInitScopeThings.add("handle404");
    _knownInitScopeThings.add(INIT_ADAPTER_TYPE);
    _knownInitScopeThings.add(INIT_ADAPTER_SELECTOR);
  }

  public static final class AppContextReachable extends ReflectionVisitor.Reachable {
    public boolean follow(Object o, Class c, java.lang.reflect.Field f) {

      if (_reachableStoppers.contains(c)) return false;

      if (f != null && _reachableStoppers.contains(f.getType())) return false;

      return super.follow(o, c, f);
    }
  }

  private static final Set<Class> _reachableStoppers = new HashSet<Class>();

  static {
    _reachableStoppers.add(HttpServer.class);
    _reachableStoppers.add(AppServer.class);
    _reachableStoppers.add(DBTCP.class);
    _reachableStoppers.add(Mongo.class);
    _reachableStoppers.add(WeakBag.class);
    _reachableStoppers.add(WeakValueMap.class);
  }
}
  public void migrate(JdbcTemplate jdbcTemplate) throws Exception {
    LOG.info("Set up initial values");

    LOG.debug("Insert default billing plan");
    SimpleJdbcInsert billingJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_billing_plan")
            .usingColumns(
                "billingType",
                "numUsers",
                "volume",
                "numProjects",
                "pricing",
                "hasBugEnable",
                "hasStandupMeetingEnable",
                "hasTimeTracking")
            .usingGeneratedKeyColumns("id");

    Map<String, Object> billingParameters = new HashMap<>();
    billingParameters.put("billingType", "Community");
    billingParameters.put("numUsers", 99999999);
    billingParameters.put("volume", 999999999999L);
    billingParameters.put("numProjects", 999999);
    billingParameters.put("pricing", 0);
    billingParameters.put("hasBugEnable", Boolean.TRUE);
    billingParameters.put("hasStandupMeetingEnable", Boolean.TRUE);
    billingParameters.put("hasTimeTracking", Boolean.TRUE);

    Number billingPlanId = billingJdbcInsert.executeAndReturnKey(billingParameters);

    LOG.debug("Insert default account");
    SimpleJdbcInsert accountJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_account")
            .usingColumns("status", "billingPlanId", "paymentMethod", "subdomain")
            .usingGeneratedKeyColumns("id");
    Map<String, Object> accountParameters = new HashMap<>();
    accountParameters.put("status", "Active");
    accountParameters.put("billingPlanId", billingPlanId);
    accountParameters.put("paymentMethod", "None");
    accountParameters.put("subdomain", "");
    Number accountId = accountJdbcInsert.executeAndReturnKey(accountParameters);

    LOG.debug("Insert default users");
    SimpleJdbcInsert userJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_user")
            .usingColumns(
                "username",
                "firstname",
                "lastname",
                "email",
                "status",
                "registeredTime",
                "password",
                "timezone");
    Date nowDate = DateTimeUtils.convertDateTimeToUTC(new GregorianCalendar().getTime());
    String timezoneDbId = TimezoneMapper.getTimezoneDbId(TimeZone.getDefault());

    Map<String, Object> userParameters = new HashMap<>();
    userParameters.put("username", "*****@*****.**");
    userParameters.put("firstname", "");
    userParameters.put("lastname", "admin");
    userParameters.put("email", "*****@*****.**");
    userParameters.put("status", "Active");
    userParameters.put("registeredTime", nowDate);
    userParameters.put("password", PasswordEncryptHelper.encryptSaltPassword("admin123"));
    userParameters.put("timezone", timezoneDbId);
    userJdbcInsert.execute(userParameters);

    LOG.debug("Insert default user avatar");

    LOG.debug("Create associate between user and billing plan");
    SimpleJdbcInsert userAccountJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_user_account")
            .usingColumns(
                "username", "accountId", "isAccountOwner", "registeredTime", "registerStatus")
            .usingGeneratedKeyColumns("id");
    Map<String, Object> userAccountParameters = new HashMap<>();
    userAccountParameters.put("username", "*****@*****.**");
    userAccountParameters.put("accountId", accountId);
    userAccountParameters.put("isAccountOwner", Boolean.TRUE);
    userAccountParameters.put("registeredTime", nowDate);
    userAccountParameters.put("registerStatus", "Active");

    userAccountJdbcInsert.executeAndReturnKey(userAccountParameters);

    LOG.debug("Insert default roles");
    SimpleJdbcInsert roleJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_roles")
            .usingColumns("rolename", "description", "sAccountId", "isSystemRole")
            .usingGeneratedKeyColumns("id");

    LOG.debug("Create default admin role");
    SqlParameterSource adminRoleParameters =
        new MapSqlParameterSource()
            .addValue("rolename", "Administrator")
            .addValue("description", "Admin Role")
            .addValue("sAccountId", accountId)
            .addValue("isSystemRole", Boolean.TRUE);
    Number adminRoleId = roleJdbcInsert.executeAndReturnKey(adminRoleParameters);

    LOG.debug("Create default employee role");
    SqlParameterSource employeeRoleParameters =
        new MapSqlParameterSource()
            .addValue("rolename", "Employee")
            .addValue("description", "Employee Role")
            .addValue("sAccountId", accountId)
            .addValue("isSystemRole", Boolean.TRUE);
    Number employeeRoleId = roleJdbcInsert.executeAndReturnKey(employeeRoleParameters);

    LOG.debug("Create default guest role");
    SqlParameterSource guestRoleParameters =
        new MapSqlParameterSource()
            .addValue("rolename", "Guest")
            .addValue("description", "Guest Role")
            .addValue("sAccountId", accountId)
            .addValue("isSystemRole", Boolean.TRUE);
    Number guestRoleId = roleJdbcInsert.executeAndReturnKey(guestRoleParameters);

    LOG.debug("Associate permission with admin role");
    SimpleJdbcInsert rolePermissionJdbcInsert =
        new SimpleJdbcInsert(jdbcTemplate)
            .withTableName("s_role_permission")
            .usingColumns("roleid", "roleVal")
            .usingGeneratedKeyColumns("id");

    SqlParameterSource adminRolePermissionParameters =
        new MapSqlParameterSource()
            .addValue("roleid", adminRoleId)
            .addValue("roleVal", PermissionMap.buildAdminPermissionCollection().toJsonString());
    rolePermissionJdbcInsert.execute(adminRolePermissionParameters);

    LOG.debug("Associate permission with employee role");
    SqlParameterSource employeeRolePermissionParameters =
        new MapSqlParameterSource()
            .addValue("roleid", employeeRoleId)
            .addValue("roleVal", PermissionMap.buildEmployeePermissionCollection().toJsonString());
    rolePermissionJdbcInsert.execute(employeeRolePermissionParameters);

    LOG.debug("Associate permission with guest role");
    SqlParameterSource guestRolePermissionParameters =
        new MapSqlParameterSource()
            .addValue("roleid", guestRoleId)
            .addValue("roleVal", PermissionMap.buildGuestPermissionCollection().toJsonString());
    rolePermissionJdbcInsert.execute(guestRolePermissionParameters);
  }
示例#19
0
    public ConfigBuilder(InputStream configXml) {
      try {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        dbFactory.setNamespaceAware(true);
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        doc = dBuilder.parse(configXml);
        doc.getDocumentElement().normalize();

        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema =
            factory.newSchema(
                new StreamSource(
                    Thread.currentThread()
                        .getContextClassLoader()
                        .getResourceAsStream("jen8583.xsd")));

        Validator validator = schema.newValidator();
        validator.validate(new DOMSource(doc));
      } catch (Exception e) {
        throw new ConfigException(e.getMessage(), e);
      }

      Element defaults = (Element) doc.getElementsByTagName(ELEMENT_DEFAULTS).item(0);

      for (Element e : getSubElements(defaults)) {
        switch (e.getTagName()) {
          case ELEMENT_VAR:
            defaultLengthEncoding =
                Encoding.valueOf(getMandatoryAttribute(e, ATTR_LENGTH_ENCODING));
            LOG.info("Default length encoding: {}", defaultLengthEncoding);
            break;
          case ELEMENT_TLV:
            defaultTlvTagEncoding = Encoding.valueOf(getMandatoryAttribute(e, ATTR_TAG_ENCODING));
            LOG.info("Default tlv tag encoding: {}", defaultTlvTagEncoding);
            defaultTlvLengthEncoding =
                Encoding.valueOf(getMandatoryAttribute(e, ATTR_LENGTH_ENCODING));
            LOG.info("Default tlv length encoding: {}", defaultTlvLengthEncoding);
            break;
          case ELEMENT_ALPHA:
            defaultTrim = Boolean.valueOf(getMandatoryAttribute(e, ATTR_TRIM));
            LOG.info("Default trim: {}", defaultTrim);
            String stringJustify = getMandatoryAttribute(e, ATTR_JUSTIFIED);
            switch (stringJustify) {
              case ATTR_CONST_LEFT:
                defaultLeftJustified = true;
                break;
              case ATTR_CONST_RIGHT:
                defaultLeftJustified = false;
                break;
              default:
                throw new ConfigException(format("Invalid value for justified: %s", stringJustify));
            }
            LOG.info(
                "Default {} justified", defaultLeftJustified ? ATTR_CONST_LEFT : ATTR_CONST_RIGHT);
            break;
          case ELEMENT_NUMERIC:
            defaultNumericEncoding = Encoding.valueOf(getMandatoryAttribute(e, ATTR_ENCODING));
            LOG.info("Default numeric encoding: {}", defaultNumericEncoding);
            break;
          case ELEMENT_DATE:
            defaultDateEncoding = Encoding.valueOf(getMandatoryAttribute(e, ATTR_ENCODING));
            LOG.info("Default date encoding: {}", defaultDateEncoding);
            String tzDefault = getMandatoryAttribute(e, ATTR_TIMEZONE);
            defaultTimeZone =
                ATTR_CONST_SYSTEM.equals(tzDefault)
                    ? TimeZone.getDefault()
                    : ZoneInfo.getTimeZone(tzDefault);
            LOG.info("Default timezone: {}", defaultTimeZone.getID());
            break;
          case ELEMENT_ORDINALITY:
            defaultMandatory = Boolean.valueOf(getMandatoryAttribute(e, ATTR_MANDATORY));
            LOG.info("Default mandatory: {}", defaultMandatory);
            defaultFailFast = Boolean.valueOf(getMandatoryAttribute(e, ATTR_FAIL_FAST));
            LOG.info("Default fail-fast: {}", defaultFailFast);
            break;
        }
      }
    }
示例#20
0
文件: ISODate.java 项目: krauz0/jPOS
 /**
  * @param d date object to be formatted
  * @return date in YDDD format suitable for bit 31 or 37 depending on interchange
  */
 public static String getJulianDate(Date d) {
   String day = formatDate(d, "DDD", TimeZone.getDefault());
   String year = formatDate(d, "yy", TimeZone.getDefault());
   year = year.substring(1);
   return year + day;
 }
示例#21
0
  /**
   * The constructor for this class has a bunch of arguments: The frame argument is required for all
   * printing in Java. The jobname appears left justified at the top of each printed page. The font
   * size is specified in points, as on-screen font sizes are. The margins are specified in inches
   * (or fractions of inches).
   */
  public HardcopyWriter(
      Frame frame,
      String jobname,
      int fontsize,
      double leftmargin,
      double rightmargin,
      double topmargin,
      double bottommargin)
      throws HardcopyWriter.PrintCanceledException {
    // Get the PrintJob object with which we'll do all the printing.
    // The call is synchronized on the static printprops object, which
    // means that only one print dialog can be popped up at a time.
    // If the user clicks Cancel in the print dialog, throw an exception.
    Toolkit toolkit = frame.getToolkit(); // get Toolkit from Frame
    synchronized (printprops) {
      job = toolkit.getPrintJob(frame, jobname, printprops);
    }
    if (job == null) throw new PrintCanceledException("User cancelled print request");

    pagesize = job.getPageDimension(); // query the page size
    pagedpi = job.getPageResolution(); // query the page resolution

    // Bug Workaround:
    // On windows, getPageDimension() and getPageResolution don't work, so
    // we've got to fake them.
    if (System.getProperty("os.name").regionMatches(true, 0, "windows", 0, 7)) {
      // Use screen dpi, which is what the PrintJob tries to emulate, anyway
      pagedpi = toolkit.getScreenResolution();
      System.out.println(pagedpi);
      // Assume a 8.5" x 11" page size.  A4 paper users have to change this.
      pagesize = new Dimension((int) (8.5 * pagedpi), 11 * pagedpi);
      System.out.println(pagesize);
      // We also have to adjust the fontsize.  It is specified in points,
      // (1 point = 1/72 of an inch) but Windows measures it in pixels.
      fontsize = fontsize * pagedpi / 72;
      System.out.println(fontsize);
      System.out.flush();
    }

    // Compute coordinates of the upper-left corner of the page.
    // I.e. the coordinates of (leftmargin, topmargin).  Also compute
    // the width and height inside of the margins.
    x0 = (int) (leftmargin * pagedpi);
    y0 = (int) (topmargin * pagedpi);
    width = pagesize.width - (int) ((leftmargin + rightmargin) * pagedpi);
    height = pagesize.height - (int) ((topmargin + bottommargin) * pagedpi);

    // Get body font and font size
    font = new Font("Monospaced", Font.PLAIN, fontsize);
    metrics = toolkit.getFontMetrics(font);
    lineheight = metrics.getHeight();
    lineascent = metrics.getAscent();
    charwidth = metrics.charWidth('0'); // Assumes a monospaced font!

    // Now compute columns and lines will fit inside the margins
    chars_per_line = width / charwidth;
    lines_per_page = height / lineheight;

    // Get header font information
    // And compute baseline of page header: 1/8" above the top margin
    headerfont = new Font("SansSerif", Font.ITALIC, fontsize);
    headermetrics = toolkit.getFontMetrics(headerfont);
    headery = y0 - (int) (0.125 * pagedpi) - headermetrics.getHeight() + headermetrics.getAscent();

    // Compute the date/time string to display in the page header
    DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT);
    df.setTimeZone(TimeZone.getDefault());
    time = df.format(new Date());

    this.jobname = jobname; // save name
    this.fontsize = fontsize; // save font size
  }
示例#22
0
文件: ISODate.java 项目: krauz0/jPOS
 /**
  * Formats a date object, using the default time zone for this host
  *
  * @param d date object to be formatted
  * @param pattern to be used for formatting
  */
 public static String formatDate(Date d, String pattern) {
   return formatDate(d, pattern, TimeZone.getDefault());
 }
 /**
  * Constructs a new GregorianCalendar representing midnight on the given date with the default
  * time zone and locale.
  *
  * @param year corresponds to the YEAR time field.
  * @param month corresponds to the MONTH time field.
  * @param day corresponds to the DAY time field.
  * @param hour corresponds to the HOUR_OF_DAY time field.
  * @param minute corresponds to the MINUTE time field.
  * @param second corresponds to the SECOND time field.
  */
 public GregorianCalendar(int year, int month, int day, int hour, int minute, int second) {
   this(TimeZone.getDefault(), Locale.getDefault(), false);
   set(year, month, day, hour, minute, second);
 }
示例#24
0
 public DummyDataContext(OptiqConnection connection) {
   this.connection = connection;
   this.map = ImmutableMap.<String, Object>of("timeZone", TimeZone.getDefault());
 }
 /**
  * Constructs a new GregorianCalender representing the current time, using the default time zone
  * and the specified locale.
  *
  * @param locale a locale.
  */
 public GregorianCalendar(Locale locale) {
   this(TimeZone.getDefault(), locale);
 }
示例#26
0
文件: ISODate.java 项目: krauz0/jPOS
 /**
  * try to find out suitable date given [YY[YY]]MMDDhhmmss format<br>
  * (difficult thing being finding out appropiate year)
  *
  * @param d date formated as [YY[YY]]MMDDhhmmss, typical field 13 + field 12
  * @param currentTime currentTime in millis
  * @return Date
  */
 public static Date parseISODate(String d, long currentTime) {
   return parseISODate(d, currentTime, TimeZone.getDefault());
 }
 /**
  * Constructs a new GregorianCalendar representing midnight on the given date with the default
  * time zone and locale.
  *
  * @param year corresponds to the YEAR time field.
  * @param month corresponds to the MONTH time field.
  * @param day corresponds to the DAY time field.
  */
 public GregorianCalendar(int year, int month, int day) {
   this(TimeZone.getDefault(), Locale.getDefault(), false);
   set(year, month, day);
 }
示例#28
0
文件: ISODate.java 项目: krauz0/jPOS
 /**
  * converts a string in DD/MM/YY HH:MM:SS format to a Date object Warning: return null on invalid
  * dates (prints Exception to console) Uses default time zone for this host
  *
  * @return parsed Date (or null)
  */
 public static Date parseDateTime(String s) {
   return parseDateTime(s, TimeZone.getDefault());
 }
示例#29
0
 /**
  * Converts windows time in minutes from 1/1/1601 to <tt>Date</tt> object.
  *
  * @param time the number of minutes from 1/1/1601
  * @return the <tt>Date</tt> object
  */
 public static Date windowsTimeToDateObject(long time) {
   // Date.parse("1/1/1601") == 11644473600000L
   long date = time * 60000 - 11644473600000L;
   date -= TimeZone.getDefault().getOffset(date);
   return new Date(date);
 }
示例#30
0
/**
 * Unit tests for implementations of Visual Basic for Applications (VBA) functions.
 *
 * <p>Every function defined in {@link Vba} must have a test here. In addition, there should be MDX
 * tests (usually in {@link mondrian.olap.fun.FunctionTest}) if handling of argument types, result
 * types, operator overloading, exception handling or null handling are non-trivial.
 *
 * @author jhyde
 * @since Dec 31, 2007
 */
public class VbaTest extends TestCase {
  private static final double SMALL = 1e-10d;
  private static final Date SAMPLE_DATE = sampleDate();

  private static final String timeZoneName = TimeZone.getDefault().getDisplayName();
  private static final boolean isPST =
      timeZoneName.equals("America/Los_Angeles") || timeZoneName.equals("Pacific Standard Time");

  // Conversion functions

  public void testCBool() {
    assertEquals(true, Vba.cBool(Boolean.TRUE)); // not quite to spec
    assertEquals(false, Vba.cBool(Boolean.FALSE)); // not quite to spec
    assertEquals(true, Vba.cBool(1.5));
    assertEquals(true, Vba.cBool("1.5"));
    assertEquals(false, Vba.cBool("0.00"));
    try {
      Object o = Vba.cBool("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "NumberFormatException");
    }
    // Per the spec, the string "true" is no different from any other
    try {
      Object o = Vba.cBool("true");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "NumberFormatException");
    }
  }

  private void assertMessage(RuntimeException e, final String expected) {
    final String message = e.getClass().getName() + ": " + e.getMessage();
    assertTrue(
        "expected message to contain '" + expected + "', got '" + message + "'",
        message.indexOf(expected) >= 0);
  }

  public void testCInt() {
    assertEquals(1, Vba.cInt(1));
    assertEquals(1, Vba.cInt(1.4));
    // CInt rounds to the nearest even number
    assertEquals(2, Vba.cInt(1.5));
    assertEquals(2, Vba.cInt(2.5));
    assertEquals(2, Vba.cInt(1.6));
    assertEquals(-1, Vba.cInt(-1.4));
    assertEquals(-2, Vba.cInt(-1.5));
    assertEquals(-2, Vba.cInt(-1.6));
    assertEquals(Integer.MAX_VALUE, Vba.cInt((double) Integer.MAX_VALUE));
    assertEquals(Integer.MIN_VALUE, Vba.cInt((double) Integer.MIN_VALUE));
    assertEquals(Short.MAX_VALUE, Vba.cInt(((float) Short.MAX_VALUE) + .4));
    assertEquals(Short.MIN_VALUE, Vba.cInt(((float) Short.MIN_VALUE) + .4));
    try {
      Object o = Vba.cInt("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "NumberFormatException");
    }
  }

  public void testInt() {
    // if negative, Int() returns the closest number less than or
    // equal to the number.
    assertEquals(1, Vba.int_(1));
    assertEquals(1, Vba.int_(1.4));
    assertEquals(1, Vba.int_(1.5));
    assertEquals(2, Vba.int_(2.5));
    assertEquals(1, Vba.int_(1.6));
    assertEquals(-2, Vba.int_(-2));
    assertEquals(-2, Vba.int_(-1.4));
    assertEquals(-2, Vba.int_(-1.5));
    assertEquals(-2, Vba.int_(-1.6));
    assertEquals(Integer.MAX_VALUE, Vba.int_((double) Integer.MAX_VALUE));
    assertEquals(Integer.MIN_VALUE, Vba.int_((double) Integer.MIN_VALUE));
    try {
      Object o = Vba.int_("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "Invalid parameter.");
    }
  }

  public void testFix() {
    // if negative, Fix() returns the closest number greater than or
    // equal to the number.
    assertEquals(1, Vba.fix(1));
    assertEquals(1, Vba.fix(1.4));
    assertEquals(1, Vba.fix(1.5));
    assertEquals(2, Vba.fix(2.5));
    assertEquals(1, Vba.fix(1.6));
    assertEquals(-1, Vba.fix(-1));
    assertEquals(-1, Vba.fix(-1.4));
    assertEquals(-1, Vba.fix(-1.5));
    assertEquals(-1, Vba.fix(-1.6));
    assertEquals(Integer.MAX_VALUE, Vba.fix((double) Integer.MAX_VALUE));
    assertEquals(Integer.MIN_VALUE, Vba.fix((double) Integer.MIN_VALUE));
    try {
      Object o = Vba.fix("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "Invalid parameter.");
    }
  }

  public void testCDbl() {
    assertEquals(1.0, Vba.cDbl(1));
    assertEquals(1.4, Vba.cDbl(1.4));
    // CInt rounds to the nearest even number
    assertEquals(1.5, Vba.cDbl(1.5));
    assertEquals(2.5, Vba.cDbl(2.5));
    assertEquals(1.6, Vba.cDbl(1.6));
    assertEquals(-1.4, Vba.cDbl(-1.4));
    assertEquals(-1.5, Vba.cDbl(-1.5));
    assertEquals(-1.6, Vba.cDbl(-1.6));
    assertEquals(Double.MAX_VALUE, Vba.cDbl(Double.MAX_VALUE));
    assertEquals(Double.MIN_VALUE, Vba.cDbl(Double.MIN_VALUE));
    try {
      Object o = Vba.cDbl("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "NumberFormatException");
    }
  }

  public void testHex() {
    assertEquals("0", Vba.hex(0));
    assertEquals("1", Vba.hex(1));
    assertEquals("A", Vba.hex(10));
    assertEquals("64", Vba.hex(100));
    assertEquals("FFFFFFFF", Vba.hex(-1));
    assertEquals("FFFFFFF6", Vba.hex(-10));
    assertEquals("FFFFFF9C", Vba.hex(-100));
    try {
      Object o = Vba.hex("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "Invalid parameter.");
    }
  }

  public void testOct() {
    assertEquals("0", Vba.oct(0));
    assertEquals("1", Vba.oct(1));
    assertEquals("12", Vba.oct(10));
    assertEquals("144", Vba.oct(100));
    assertEquals("37777777777", Vba.oct(-1));
    assertEquals("37777777766", Vba.oct(-10));
    assertEquals("37777777634", Vba.oct(-100));
    try {
      Object o = Vba.oct("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "Invalid parameter.");
    }
  }

  public void testStr() {
    assertEquals(" 0", Vba.str(0));
    assertEquals(" 1", Vba.str(1));
    assertEquals(" 10", Vba.str(10));
    assertEquals(" 100", Vba.str(100));
    assertEquals("-1", Vba.str(-1));
    assertEquals("-10", Vba.str(-10));
    assertEquals("-100", Vba.str(-100));
    assertEquals("-10.123", Vba.str(-10.123));
    assertEquals(" 10.123", Vba.str(10.123));
    try {
      Object o = Vba.oct("a");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "Invalid parameter.");
    }
  }

  public void testVal() {
    assertEquals(-1615198.0, Vba.val(" -  1615 198th Street N.E."));
    assertEquals(1615198.0, Vba.val(" 1615 198th Street N.E."));
    assertEquals(1615.198, Vba.val(" 1615 . 198th Street N.E."));
    assertEquals(1615.19, Vba.val(" 1615 . 19 . 8th Street N.E."));
    assertEquals((double) 0xffff, Vba.val("&HFFFF"));
    assertEquals(668.0, Vba.val("&O1234"));
  }

  public void testCDate() throws ParseException {
    Date date = new Date();
    assertEquals(date, Vba.cDate(date));
    assertNull(Vba.cDate(null));
    // CInt rounds to the nearest even number
    try {
      assertEquals(DateFormat.getDateInstance().parse("Jan 12, 1952"), Vba.cDate("Jan 12, 1952"));
      assertEquals(
          DateFormat.getDateInstance().parse("October 19, 1962"), Vba.cDate("October 19, 1962"));
      assertEquals(DateFormat.getTimeInstance().parse("4:35:47 PM"), Vba.cDate("4:35:47 PM"));
      assertEquals(
          DateFormat.getDateTimeInstance().parse("October 19, 1962 4:35:47 PM"),
          Vba.cDate("October 19, 1962 4:35:47 PM"));
    } catch (ParseException e) {
      e.printStackTrace();
      fail();
    }

    try {
      Vba.cDate("Jan, 1952");
      fail();
    } catch (InvalidArgumentException e) {
      assertTrue(e.getMessage().indexOf("Jan, 1952") >= 0);
    }
  }

  public void testIsDate() throws ParseException {
    // CInt rounds to the nearest even number
    assertFalse(Vba.isDate(null));
    assertTrue(Vba.isDate(new Date()));
    assertTrue(Vba.isDate("Jan 12, 1952"));
    assertTrue(Vba.isDate("October 19, 1962"));
    assertTrue(Vba.isDate("4:35:47 PM"));
    assertTrue(Vba.isDate("October 19, 1962 4:35:47 PM"));
    assertFalse(Vba.isDate("Jan, 1952"));
  }

  // DateTime

  public void testDateAdd() {
    assertEquals("2008/04/24 19:10:45", SAMPLE_DATE);

    // 2008-02-01 0:00:00
    Calendar calendar = Calendar.getInstance();

    calendar.set(2007, 1 /* 0-based! */, 1, 0, 0, 0);
    final Date feb2007 = calendar.getTime();
    assertEquals("2007/02/01 00:00:00", feb2007);

    assertEquals("2008/04/24 19:10:45", Vba.dateAdd("yyyy", 0, SAMPLE_DATE));
    assertEquals("2009/04/24 19:10:45", Vba.dateAdd("yyyy", 1, SAMPLE_DATE));
    assertEquals("2006/04/24 19:10:45", Vba.dateAdd("yyyy", -2, SAMPLE_DATE));
    // partial years interpolate
    final Date sampleDatePlusTwoPointFiveYears = Vba.dateAdd("yyyy", 2.5, SAMPLE_DATE);
    if (isPST) {
      // Only run test in PST, because test would produce different
      // results if start and end are not both in daylight savings time.
      final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
      final String dateString = dateFormat.format(sampleDatePlusTwoPointFiveYears);
      // We allow "2010/10/24 07:10:45" for computers that have an out of
      // date timezone database. 2010/10/24 is in daylight savings time,
      // but was not according to the old rules.
      assertTrue(
          "Got " + dateString,
          dateString.equals("2010/10/24 06:40:45") || dateString.equals("2010/10/24 07:10:45"));
    }
    assertEquals("2009/01/24 19:10:45", Vba.dateAdd("q", 3, SAMPLE_DATE));

    // partial months are interesting!
    assertEquals("2008/06/24 19:10:45", Vba.dateAdd("m", 2, SAMPLE_DATE));
    assertEquals("2007/01/01 00:00:00", Vba.dateAdd("m", -1, feb2007));
    assertEquals("2007/03/01 00:00:00", Vba.dateAdd("m", 1, feb2007));
    assertEquals("2007/02/08 00:00:00", Vba.dateAdd("m", .25, feb2007));
    // feb 2008 is a leap month, so a quarter month is 7.25 days
    assertEquals("2008/02/08 06:00:00", Vba.dateAdd("m", 12.25, feb2007));

    assertEquals("2008/05/01 19:10:45", Vba.dateAdd("y", 7, SAMPLE_DATE));
    assertEquals("2008/05/02 01:10:45", Vba.dateAdd("y", 7.25, SAMPLE_DATE));
    assertEquals("2008/04/24 23:10:45", Vba.dateAdd("h", 4, SAMPLE_DATE));
    assertEquals("2008/04/24 20:00:45", Vba.dateAdd("n", 50, SAMPLE_DATE));
    assertEquals("2008/04/24 19:10:36", Vba.dateAdd("s", -9, SAMPLE_DATE));
  }

  public void testDateDiff() {
    // TODO:
  }

  public void testDatePart2() {
    assertEquals(2008, Vba.datePart("yyyy", SAMPLE_DATE));
    assertEquals(2, Vba.datePart("q", SAMPLE_DATE)); // 2nd quarter
    assertEquals(4, Vba.datePart("m", SAMPLE_DATE));
    assertEquals(5, Vba.datePart("w", SAMPLE_DATE)); // thursday
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE));
    assertEquals(115, Vba.datePart("y", SAMPLE_DATE));
    assertEquals(19, Vba.datePart("h", SAMPLE_DATE));
    assertEquals(10, Vba.datePart("n", SAMPLE_DATE));
    assertEquals(45, Vba.datePart("s", SAMPLE_DATE));
  }

  public void testDatePart3() {
    assertEquals(5, Vba.datePart("w", SAMPLE_DATE, Calendar.SUNDAY));
    assertEquals(4, Vba.datePart("w", SAMPLE_DATE, Calendar.MONDAY));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.WEDNESDAY));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.THURSDAY));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.FRIDAY));
  }

  public void testDatePart4() {
    // 2008 starts on a Tuesday
    // 2008-04-29 is a Thursday
    // That puts it in week 17 by most ways of computing weeks
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY, 0));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY, 2));
    assertEquals(16, Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY, 3));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.MONDAY, 0));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.MONDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.MONDAY, 2));
    assertEquals(16, Vba.datePart("ww", SAMPLE_DATE, Calendar.MONDAY, 3));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.TUESDAY, 0));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.TUESDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.TUESDAY, 2));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.TUESDAY, 3));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.WEDNESDAY, 0));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.WEDNESDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.WEDNESDAY, 2));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.WEDNESDAY, 3));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.THURSDAY, 0));
    assertEquals(18, Vba.datePart("ww", SAMPLE_DATE, Calendar.THURSDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.THURSDAY, 2));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.THURSDAY, 3));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.FRIDAY, 0));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.FRIDAY, 1));
    assertEquals(16, Vba.datePart("ww", SAMPLE_DATE, Calendar.FRIDAY, 2));
    assertEquals(16, Vba.datePart("ww", SAMPLE_DATE, Calendar.FRIDAY, 3));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SATURDAY, 0));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SATURDAY, 1));
    assertEquals(17, Vba.datePart("ww", SAMPLE_DATE, Calendar.SATURDAY, 2));
    assertEquals(16, Vba.datePart("ww", SAMPLE_DATE, Calendar.SATURDAY, 3));
    try {
      int i = Vba.datePart("ww", SAMPLE_DATE, Calendar.SUNDAY, 4);
      fail("expected error, got " + i);
    } catch (RuntimeException e) {
      assertMessage(e, "ArrayIndexOutOfBoundsException");
    }
  }

  public void testDate() {
    final Date date = Vba.date();
    assertNotNull(date);
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
    assertEquals(0, calendar.get(Calendar.MILLISECOND));
  }

  public void testDateSerial() {
    final Date date = Vba.dateSerial(2008, 2, 1);
    assertEquals("2008/02/01 00:00:00", date);
  }

  private void assertEquals(String expected, Date date) {
    final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US);
    final String dateString = dateFormat.format(date);
    assertEquals(expected, dateString);
  }

  public void testFormatDateTime() {
    try {
      Date date = DateFormat.getDateTimeInstance().parse("October 19, 1962 4:35:47 PM");
      assertEquals("Oct 19, 1962 4:35:47 PM", Vba.formatDateTime(date));
      assertEquals("Oct 19, 1962 4:35:47 PM", Vba.formatDateTime(date, 0));
      assertEquals("October 19, 1962", Vba.formatDateTime(date, 1));
      assertEquals("10/19/62", Vba.formatDateTime(date, 2));
      String datestr = Vba.formatDateTime(date, 3);
      assertNotNull(datestr);
      // skip the timezone so this test runs everywhere
      // in EST, this string is "4:35:47 PM EST"
      assertTrue(datestr.startsWith("4:35:47 PM"));
      assertEquals("4:35 PM", Vba.formatDateTime(date, 4));
    } catch (ParseException e) {
      e.printStackTrace();
      fail();
    }
  }

  public void testDateValue() {
    Date date = new Date();
    final Date date1 = Vba.dateValue(date);
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date1);
    assertEquals(0, calendar.get(Calendar.HOUR_OF_DAY));
    assertEquals(0, calendar.get(Calendar.MINUTE));
    assertEquals(0, calendar.get(Calendar.SECOND));
    assertEquals(0, calendar.get(Calendar.MILLISECOND));
  }

  private static Date sampleDate() {
    Calendar calendar = Calendar.getInstance();
    // Thursday 2008-04-24 7:10:45pm
    // Chose a Thursday because 2008 starts on a Tuesday - it makes weeks
    // interesting.
    calendar.set(2008, 3 /* 0-based! */, 24, 19, 10, 45);
    return calendar.getTime();
  }

  public void testDay() {
    assertEquals(24, Vba.day(SAMPLE_DATE));
  }

  public void testHour() {
    assertEquals(19, Vba.hour(SAMPLE_DATE));
  }

  public void testMinute() {
    assertEquals(10, Vba.minute(SAMPLE_DATE));
  }

  public void testMonth() {
    assertEquals(4, Vba.month(SAMPLE_DATE));
  }

  public void testNow() {
    final Date date = Vba.now();
    assertNotNull(date);
  }

  public void testSecond() {
    assertEquals(45, Vba.second(SAMPLE_DATE));
  }

  public void testTimeSerial() {
    final Date date = Vba.timeSerial(17, 42, 10);
    assertEquals("1970/01/01 17:42:10", date);
  }

  public void testTimeValue() {
    assertEquals("1970/01/01 19:10:45", Vba.timeValue(SAMPLE_DATE));
  }

  public void testTimer() {
    final float v = Vba.timer();
    assertTrue(v >= 0);
    assertTrue(v < 24 * 60 * 60);
  }

  public void testWeekday1() {
    if (Calendar.getInstance().getFirstDayOfWeek() == Calendar.SUNDAY) {
      assertEquals(Calendar.THURSDAY, Vba.weekday(SAMPLE_DATE));
    }
  }

  public void testWeekday2() {
    // 2008/4/24 falls on a Thursday.

    // If Sunday is the first day of the week, Thursday is day 5.
    assertEquals(5, Vba.weekday(SAMPLE_DATE, Calendar.SUNDAY));

    // If Monday is the first day of the week, then 2008/4/24 falls on the
    // 4th day of the week
    assertEquals(4, Vba.weekday(SAMPLE_DATE, Calendar.MONDAY));

    assertEquals(3, Vba.weekday(SAMPLE_DATE, Calendar.TUESDAY));
    assertEquals(2, Vba.weekday(SAMPLE_DATE, Calendar.WEDNESDAY));
    assertEquals(1, Vba.weekday(SAMPLE_DATE, Calendar.THURSDAY));
    assertEquals(7, Vba.weekday(SAMPLE_DATE, Calendar.FRIDAY));
    assertEquals(6, Vba.weekday(SAMPLE_DATE, Calendar.SATURDAY));
  }

  public void testYear() {
    assertEquals(2008, Vba.year(SAMPLE_DATE));
  }

  public void testFormatNumber() {
    assertEquals("1", Vba.formatNumber(1.0));
    assertEquals("1.0", Vba.formatNumber(1.0, 1));

    assertEquals("0.1", Vba.formatNumber(0.1, -1, -1));
    assertEquals(".1", Vba.formatNumber(0.1, -1, 0));
    assertEquals("0.1", Vba.formatNumber(0.1, -1, 1));

    assertEquals("-1", Vba.formatNumber(-1, -1, 1, -1));
    assertEquals("-1", Vba.formatNumber(-1, -1, 1, 0));
    assertEquals("(1)", Vba.formatNumber(-1, -1, 1, 1));

    assertEquals("1", Vba.formatNumber(1, -1, 1, -1));
    assertEquals("1", Vba.formatNumber(1, -1, 1, 0));
    assertEquals("1", Vba.formatNumber(1, -1, 1, 1));

    assertEquals("1,000", Vba.formatNumber(1000.0, -1, -1, -1, -1));
    assertEquals("1000.0", Vba.formatNumber(1000.0, 1, -1, -1, 0));
    assertEquals("1,000.0", Vba.formatNumber(1000.0, 1, -1, -1, 1));
  }

  public void testFormatPercent() {
    assertEquals("100%", Vba.formatPercent(1.0));
    assertEquals("100.0%", Vba.formatPercent(1.0, 1));

    assertEquals("0.1%", Vba.formatPercent(0.001, 1, -1));
    assertEquals(".1%", Vba.formatPercent(0.001, 1, 0));
    assertEquals("0.1%", Vba.formatPercent(0.001, 1, 1));

    assertEquals("11%", Vba.formatPercent(0.111, -1));
    assertEquals("11%", Vba.formatPercent(0.111, 0));
    assertEquals("11.100%", Vba.formatPercent(0.111, 3));

    assertEquals("-100%", Vba.formatPercent(-1, -1, 1, -1));
    assertEquals("-100%", Vba.formatPercent(-1, -1, 1, 0));
    assertEquals("(100%)", Vba.formatPercent(-1, -1, 1, 1));

    assertEquals("100%", Vba.formatPercent(1, -1, 1, -1));
    assertEquals("100%", Vba.formatPercent(1, -1, 1, 0));
    assertEquals("100%", Vba.formatPercent(1, -1, 1, 1));

    assertEquals("100,000%", Vba.formatPercent(1000.0, -1, -1, -1, -1));
    assertEquals("100000.0%", Vba.formatPercent(1000.0, 1, -1, -1, 0));
    assertEquals("100,000.0%", Vba.formatPercent(1000.0, 1, -1, -1, 1));
  }

  public void testFormatCurrency() {
    assertEquals("$1.00", Vba.formatCurrency(1.0));
    assertEquals("$0.00", Vba.formatCurrency(0.0));
    assertEquals("$1.0", Vba.formatCurrency(1.0, 1));
    assertEquals("$1", Vba.formatCurrency(1.0, 0));
    assertEquals("$.10", Vba.formatCurrency(0.10, -1, 0));
    assertEquals("$0.10", Vba.formatCurrency(0.10, -1, -1));
    // todo: still need to implement parens customization
    // assertEquals("-$0.10", Vba.formatCurrency(-0.10, -1, -1, -1));
    assertEquals("($0.10)", Vba.formatCurrency(-0.10, -1, -1, 0));

    assertEquals("$1,000.00", Vba.formatCurrency(1000.0, -1, -1, 0, 0));
    assertEquals("$1000.00", Vba.formatCurrency(1000.0, -1, -1, 0, -1));
  }

  public void testTypeName() {
    assertEquals("Double", Vba.typeName(1.0));
    assertEquals("Integer", Vba.typeName(1));
    assertEquals("Float", Vba.typeName(1.0f));
    assertEquals("Byte", Vba.typeName((byte) 1));
    assertEquals("NULL", Vba.typeName(null));
    assertEquals("String", Vba.typeName(""));
    assertEquals("Date", Vba.typeName(new Date()));
  }

  // Financial

  public void testFv() {
    double f, r, y, p, x;
    int n;
    boolean t;

    r = 0;
    n = 3;
    y = 2;
    p = 7;
    t = true;
    f = Vba.fV(r, n, y, p, t);
    x = -13;
    assertEquals(x, f);

    r = 1;
    n = 10;
    y = 100;
    p = 10000;
    t = false;
    f = Vba.fV(r, n, y, p, t);
    x = -10342300;
    assertEquals(x, f);

    r = 1;
    n = 10;
    y = 100;
    p = 10000;
    t = true;
    f = Vba.fV(r, n, y, p, t);
    x = -10444600;
    assertEquals(x, f);

    r = 2;
    n = 12;
    y = 120;
    p = 12000;
    t = false;
    f = Vba.fV(r, n, y, p, t);
    x = -6409178400d;
    assertEquals(x, f);

    r = 2;
    n = 12;
    y = 120;
    p = 12000;
    t = true;
    f = Vba.fV(r, n, y, p, t);
    x = -6472951200d;
    assertEquals(x, f);

    // cross tests with pv
    r = 2.95;
    n = 13;
    y = 13000;
    p = -4406.78544294496;
    t = false;
    f = Vba.fV(r, n, y, p, t);
    x = 333891.230010986; // as returned by excel
    assertEquals(x, f, 1e-2);

    r = 2.95;
    n = 13;
    y = 13000;
    p = -17406.7852148156;
    t = true;
    f = Vba.fV(r, n, y, p, t);
    x = 333891.230102539; // as returned by excel
    assertEquals(x, f, 1e-2);
  }

  public void testNpv() {
    double r, v[], npv, x;

    r = 1;
    v = new double[] {100, 200, 300, 400};
    npv = Vba.nPV(r, v);
    x = 162.5;
    assertEquals(x, npv);

    r = 2.5;
    v = new double[] {1000, 666.66666, 333.33, 12.2768416};
    npv = Vba.nPV(r, v);
    x = 347.99232604144827;
    assertEquals(x, npv, SMALL);

    r = 12.33333;
    v = new double[] {1000, 0, -900, -7777.5765};
    npv = Vba.nPV(r, v);
    x = 74.3742433377061;
    assertEquals(x, npv, 1e-12);

    r = 0.05;
    v = new double[] {200000, 300000.55, 400000, 1000000, 6000000, 7000000, -300000};
    npv = Vba.nPV(r, v);
    x = 11342283.4233124;
    assertEquals(x, npv, 1e-8);
  }

  public void testPmt() {
    double f, r, y, p, x;
    int n;
    boolean t;

    r = 0;
    n = 3;
    p = 2;
    f = 7;
    t = true;
    y = Vba.pmt(r, n, p, f, t);
    x = -3;
    assertEquals(x, y);

    // cross check with pv
    r = 1;
    n = 10;
    p = -109.66796875;
    f = 10000;
    t = false;
    y = Vba.pmt(r, n, p, f, t);
    x = 100;
    assertEquals(x, y);

    r = 1;
    n = 10;
    p = -209.5703125;
    f = 10000;
    t = true;
    y = Vba.pmt(r, n, p, f, t);
    x = 100;
    assertEquals(x, y);

    // cross check with fv
    r = 2;
    n = 12;
    f = -6409178400d;
    p = 12000;
    t = false;
    y = Vba.pmt(r, n, p, f, t);
    x = 120;
    assertEquals(x, y);

    r = 2;
    n = 12;
    f = -6472951200d;
    p = 12000;
    t = true;
    y = Vba.pmt(r, n, p, f, t);
    x = 120;
    assertEquals(x, y);
  }

  public void testPv() {
    double f, r, y, p, x;
    int n;
    boolean t;

    r = 0;
    n = 3;
    y = 2;
    f = 7;
    t = true;
    f = Vba.pV(r, n, y, f, t);
    x = -13;
    assertEquals(x, f);

    r = 1;
    n = 10;
    y = 100;
    f = 10000;
    t = false;
    p = Vba.pV(r, n, y, f, t);
    x = -109.66796875;
    assertEquals(x, p);

    r = 1;
    n = 10;
    y = 100;
    f = 10000;
    t = true;
    p = Vba.pV(r, n, y, f, t);
    x = -209.5703125;
    assertEquals(x, p);

    r = 2.95;
    n = 13;
    y = 13000;
    f = 333891.23;
    t = false;
    p = Vba.pV(r, n, y, f, t);
    x = -4406.78544294496;
    assertEquals(x, p, 1e-10);

    r = 2.95;
    n = 13;
    y = 13000;
    f = 333891.23;
    t = true;
    p = Vba.pV(r, n, y, f, t);
    x = -17406.7852148156;
    assertEquals(x, p, 1e-10);

    // cross tests with fv
    r = 2;
    n = 12;
    y = 120;
    f = -6409178400d;
    t = false;
    p = Vba.pV(r, n, y, f, t);
    x = 12000;
    assertEquals(x, p);

    r = 2;
    n = 12;
    y = 120;
    f = -6472951200d;
    t = true;
    p = Vba.pV(r, n, y, f, t);
    x = 12000;
    assertEquals(x, p);
  }

  public void testDdb() {
    double cost, salvage, life, period, factor, result;
    cost = 100;
    salvage = 0;
    life = 10;
    period = 1;
    factor = 2;
    result = Vba.dDB(cost, salvage, life, period, factor);
    assertEquals(20.0, result);
    result = Vba.dDB(cost, salvage, life, period + 1, factor);
    assertEquals(40.0, result);
    result = Vba.dDB(cost, salvage, life, period + 2, factor);
    assertEquals(60.0, result);
    result = Vba.dDB(cost, salvage, life, period + 3, factor);
    assertEquals(80.0, result);
  }

  public void testRate() {
    double nPer, pmt, PV, fv, guess, result;
    boolean type = false;
    nPer = 12 * 30;
    pmt = -877.57;
    PV = 100000;
    fv = 0;
    guess = 0.10 / 12;
    result = Vba.rate(nPer, pmt, PV, fv, type, guess);

    // compare rate to pV calculation
    double expRate = 0.0083333;
    double expPV = Vba.pV(expRate, 12 * 30, -877.57, 0, false);
    result = Vba.rate(12 * 30, -877.57, expPV, 0, false, 0.10 / 12);
    assertTrue(Math.abs(expRate - result) < 0.0000001);

    // compare rate to fV calculation
    double expFV = Vba.fV(expRate, 12, -100, 0, false);
    result = Vba.rate(12, -100, 0, expFV, false, 0.10 / 12);
    assertTrue(Math.abs(expRate - result) < 0.0000001);
  }

  public void testIRR() {
    double vals[] = {-1000, 50, 50, 50, 50, 50, 1050};
    assertTrue(Math.abs(0.05 - Vba.IRR(vals, 0.1)) < 0.0000001);

    vals = new double[] {-1000, 200, 200, 200, 200, 200, 200};
    assertTrue(Math.abs(0.05471796 - Vba.IRR(vals, 0.1)) < 0.0000001);

    // what happens if the numbers are inversed? this may not be
    // accurate

    vals = new double[] {1000, -200, -200, -200, -200, -200, -200};
    assertTrue(Math.abs(0.05471796 - Vba.IRR(vals, 0.1)) < 0.0000001);
  }

  public void testMIRR() {
    double vals[] = {-1000, 50, 50, 50, 50, 50, 1050};
    assertTrue(Math.abs(0.05 - Vba.MIRR(vals, 0.05, 0.05)) < 0.0000001);

    vals = new double[] {-1000, 200, 200, 200, 200, 200, 200};
    assertTrue(Math.abs(0.05263266 - Vba.MIRR(vals, 0.05, 0.05)) < 0.0000001);

    vals = new double[] {-1000, 200, 200, 200, 200, 200, 200};
    assertTrue(Math.abs(0.04490701 - Vba.MIRR(vals, 0.06, 0.04)) < 0.0000001);
  }

  public void testIPmt() {
    assertEquals(-10000.0, Vba.iPmt(0.10, 1, 30, 100000, 0, false));
    assertEquals(-2185.473324557822, Vba.iPmt(0.10, 15, 30, 100000, 0, false));
    assertEquals(-60.79248252633988, Vba.iPmt(0.10, 30, 30, 100000, 0, false));
  }

  public void testPPmt() {
    assertEquals(-607.9248252633897, Vba.pPmt(0.10, 1, 30, 100000, 0, false));
    assertEquals(-8422.451500705567, Vba.pPmt(0.10, 15, 30, 100000, 0, false));
    assertEquals(-10547.13234273705, Vba.pPmt(0.10, 30, 30, 100000, 0, false));

    // verify that pmt, ipmt, and ppmt add up
    double pmt = Vba.pmt(0.10, 30, 100000, 0, false);
    double ipmt = Vba.iPmt(0.10, 15, 30, 100000, 0, false);
    double ppmt = Vba.pPmt(0.10, 15, 30, 100000, 0, false);
    assertTrue(Math.abs(pmt - (ipmt + ppmt)) < 0.0000001);
  }

  public void testSLN() {
    assertEquals(18.0, Vba.sLN(100, 10, 5));
    assertEquals(Double.POSITIVE_INFINITY, Vba.sLN(100, 10, 0));
  }

  public void testSYD() {
    assertEquals(300.0, Vba.sYD(1000, 100, 5, 5));
    assertEquals(240.0, Vba.sYD(1000, 100, 4, 5));
    assertEquals(180.0, Vba.sYD(1000, 100, 3, 5));
    assertEquals(120.0, Vba.sYD(1000, 100, 2, 5));
    assertEquals(60.0, Vba.sYD(1000, 100, 1, 5));
  }

  public void testInStr() {
    assertEquals(1, Vba.inStr("the quick brown fox jumps over the lazy dog", "the"));
    assertEquals(32, Vba.inStr(16, "the quick brown fox jumps over the lazy dog", "the"));
    assertEquals(0, Vba.inStr(16, "the quick brown fox jumps over the lazy dog", "cat"));
    assertEquals(0, Vba.inStr(1, "the quick brown fox jumps over the lazy dog", "cat"));
    assertEquals(0, Vba.inStr(1, "", "cat"));
    assertEquals(0, Vba.inStr(100, "short string", "str"));
    try {
      Vba.inStr(0, "the quick brown fox jumps over the lazy dog", "the");
      fail();
    } catch (InvalidArgumentException e) {
      assertTrue(e.getMessage().indexOf("-1 or a location") >= 0);
    }
  }

  public void testInStrRev() {
    assertEquals(32, Vba.inStrRev("the quick brown fox jumps over the lazy dog", "the"));
    assertEquals(1, Vba.inStrRev("the quick brown fox jumps over the lazy dog", "the", 16));
    try {
      Vba.inStrRev("the quick brown fox jumps over the lazy dog", "the", 0);
      fail();
    } catch (InvalidArgumentException e) {
      assertTrue(e.getMessage().indexOf("-1 or a location") >= 0);
    }
  }

  public void testStrComp() {
    assertEquals(-1, Vba.strComp("a", "b", 0));
    assertEquals(0, Vba.strComp("a", "a", 0));
    assertEquals(1, Vba.strComp("b", "a", 0));
  }

  public void testNper() {
    double f, r, y, p, x, n;
    boolean t;

    r = 0;
    y = 7;
    p = 2;
    f = 3;
    t = false;
    n = Vba.nPer(r, y, p, f, t);
    // can you believe it? excel returns nper as a fraction!??
    x = -0.71428571429;
    assertEquals(x, n, 1e-10);

    // cross check with pv
    r = 1;
    y = 100;
    p = -109.66796875;
    f = 10000;
    t = false;
    n = Vba.nPer(r, y, p, f, t);
    x = 10;
    assertEquals(x, n, 1e-12);

    r = 1;
    y = 100;
    p = -209.5703125;
    f = 10000;
    t = true;
    n = Vba.nPer(r, y, p, f, t);
    x = 10;
    assertEquals(x, n, 1e-14);

    // cross check with fv
    r = 2;
    y = 120;
    f = -6409178400d;
    p = 12000;
    t = false;
    n = Vba.nPer(r, y, p, f, t);
    x = 12;
    assertEquals(x, n, SMALL);

    r = 2;
    y = 120;
    f = -6472951200d;
    p = 12000;
    t = true;
    n = Vba.nPer(r, y, p, f, t);
    x = 12;
    assertEquals(x, n, SMALL);
  }

  // String functions

  public void testAsc() {
    assertEquals(0x61, Vba.asc("abc"));
    assertEquals(0x1234, Vba.asc("\u1234abc"));
    try {
      Object o = Vba.asc("");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "StringIndexOutOfBoundsException");
    }
  }

  public void testAscB() {
    assertEquals(0x61, Vba.ascB("abc"));
    assertEquals(0x34, Vba.ascB("\u1234abc")); // not sure about this
    try {
      Object o = Vba.ascB("");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "StringIndexOutOfBoundsException");
    }
  }

  public void testAscW() {
    // ascW behaves identically to asc
    assertEquals(0x61, Vba.ascW("abc"));
    assertEquals(0x1234, Vba.ascW("\u1234abc"));
    try {
      Object o = Vba.ascW("");
      fail("expected error, got " + o);
    } catch (RuntimeException e) {
      assertMessage(e, "StringIndexOutOfBoundsException");
    }
  }

  public void testChr() {
    assertEquals("a", Vba.chr(0x61));
    assertEquals("\u1234", Vba.chr(0x1234));
  }

  public void testChrB() {
    assertEquals("a", Vba.chrB(0x61));
    assertEquals("\u0034", Vba.chrB(0x1234));
  }

  public void testChrW() {
    assertEquals("a", Vba.chrW(0x61));
    assertEquals("\u1234", Vba.chrW(0x1234));
  }

  public void testLCase() {
    assertEquals("", Vba.lCase(""));
    assertEquals("abc", Vba.lCase("AbC"));
  }

  // NOTE: BuiltinFunTable already implements Left; todo: use this
  public void testLeft() {
    assertEquals("abc", Vba.left("abcxyz", 3));
    // length=0 is OK
    assertEquals("", Vba.left("abcxyz", 0));
    // Spec says: "If greater than or equal to the number of characters in
    // string, the entire string is returned."
    assertEquals("abcxyz", Vba.left("abcxyz", 8));
    assertEquals("", Vba.left("", 3));

    // Length<0 is illegal.
    // Note: SSAS 2005 allows length<0, giving the same result as length=0.
    // We favor the VBA spec over SSAS 2005.
    if (Bug.Ssas2005Compatible) {
      assertEquals("", Vba.left("xyz", -2));
    } else {
      try {
        String s = Vba.left("xyz", -2);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(e, "StringIndexOutOfBoundsException");
      }
    }

    assertEquals("Hello", Vba.left("Hello World!", 5));
  }

  public void testLTrim() {
    assertEquals("", Vba.lTrim(""));
    assertEquals("", Vba.lTrim("  "));
    assertEquals("abc  \r", Vba.lTrim(" \n\tabc  \r"));
  }

  public void testMid() {
    String testString = "Mid Function Demo";
    assertEquals("Mid", Vba.mid(testString, 1, 3));
    assertEquals("Demo", Vba.mid(testString, 14, 4));
    // It's OK if start+length = string.length
    assertEquals("Demo", Vba.mid(testString, 14, 5));
    // It's OK if start+length > string.length
    assertEquals("Demo", Vba.mid(testString, 14, 500));
    assertEquals("Function Demo", Vba.mid(testString, 5));
    assertEquals("o", Vba.mid("yahoo", 5, 1));

    // Start=0 illegal
    // Note: SSAS 2005 accepts start<=0, treating it as 1, therefore gives
    // different results. We favor the VBA spec over SSAS 2005.
    if (Bug.Ssas2005Compatible) {
      assertEquals("Mid Function Demo", Vba.mid(testString, 0));
      assertEquals("Mid Function Demo", Vba.mid(testString, -2));
      assertEquals("Mid Function Demo", Vba.mid(testString, -2, 5));
    } else {
      try {
        String s = Vba.mid(testString, 0);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(
            e, "Invalid parameter. Start parameter of Mid function must " + "be positive");
      }
      // Start<0 illegal
      try {
        String s = Vba.mid(testString, -2);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(
            e, "Invalid parameter. Start parameter of Mid function must " + "be positive");
      }
      // Start<0 illegal to 3 args version
      try {
        String s = Vba.mid(testString, -2, 5);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(
            e, "Invalid parameter. Start parameter of Mid function must " + "be positive");
      }
    }

    // Length=0 OK
    assertEquals("", Vba.mid(testString, 14, 0));

    // Length<0 illegal
    // Note: SSAS 2005 accepts length<0, treating it as 0, therefore gives
    // different results. We favor the VBA spec over SSAS 2005.
    if (Bug.Ssas2005Compatible) {
      assertEquals("", Vba.mid(testString, 14, -1));
    } else {
      try {
        String s = Vba.mid(testString, 14, -1);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(
            e, "Invalid parameter. Length parameter of Mid function must " + "be non-negative");
      }
    }
  }

  public void testMonthName() {
    assertEquals("January", Vba.monthName(1, false));
    assertEquals("Jan", Vba.monthName(1, true));
    assertEquals("Dec", Vba.monthName(12, true));
    try {
      String s = Vba.monthName(0, true);
      fail("expected error, got " + s);
    } catch (RuntimeException e) {
      assertMessage(e, "ArrayIndexOutOfBoundsException");
    }
  }

  public void testReplace3() {
    // replace with longer string
    assertEquals("abczabcz", Vba.replace("xyzxyz", "xy", "abc"));
    // replace with shorter string
    assertEquals("wazwaz", Vba.replace("wxyzwxyz", "xy", "a"));
    // replace with string which contains seek
    assertEquals("wxyz", Vba.replace("xyz", "xy", "wxy"));
    // replace with string which combines with following char to make seek
    assertEquals("wxyzwx", Vba.replace("xyyzxy", "xy", "wx"));
    // replace with empty string
    assertEquals("wxyza", Vba.replace("wxxyyzxya", "xy", ""));
  }

  public void testReplace4() {
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 1));
    assertEquals("xyzaz", Vba.replace("xyzxyz", "xy", "a", 2));
    assertEquals("xyzxyz", Vba.replace("xyzxyz", "xy", "a", 30));
    // spec doesn't say, but assume starting before start of string is ok
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 0));
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", -5));
  }

  public void testReplace5() {
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 1, -1));
    assertEquals("azxyz", Vba.replace("xyzxyz", "xy", "a", 1, 1));
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 1, 2));
    assertEquals("xyzazxyz", Vba.replace("xyzxyzxyz", "xy", "a", 2, 1));
  }

  public void testReplace6() {
    // compare is currently ignored
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 1, -1, 1000));
    assertEquals("azxyz", Vba.replace("xyzxyz", "xy", "a", 1, 1, 0));
    assertEquals("azaz", Vba.replace("xyzxyz", "xy", "a", 1, 2, -6));
    assertEquals("xyzazxyz", Vba.replace("xyzxyzxyz", "xy", "a", 2, 1, 11));
  }

  public void testRight() {
    assertEquals("xyz", Vba.right("abcxyz", 3));
    // length=0 is OK
    assertEquals("", Vba.right("abcxyz", 0));
    // Spec says: "If greater than or equal to the number of characters in
    // string, the entire string is returned."
    assertEquals("abcxyz", Vba.right("abcxyz", 8));
    assertEquals("", Vba.right("", 3));

    // The VBA spec says that length<0 is error.
    // Note: SSAS 2005 allows length<0, giving the same result as length=0.
    // We favor the VBA spec over SSAS 2005.
    if (Bug.Ssas2005Compatible) {
      assertEquals("", Vba.right("xyz", -2));
    } else {
      try {
        String s = Vba.right("xyz", -2);
        fail("expected error, got " + s);
      } catch (RuntimeException e) {
        assertMessage(e, "StringIndexOutOfBoundsException");
      }
    }

    assertEquals("World!", Vba.right("Hello World!", 6));
  }

  public void testRTrim() {
    assertEquals("", Vba.rTrim(""));
    assertEquals("", Vba.rTrim("  "));
    assertEquals(" \n\tabc", Vba.rTrim(" \n\tabc"));
    assertEquals(" \n\tabc", Vba.rTrim(" \n\tabc  \r"));
  }

  public void testSpace() {
    assertEquals("   ", Vba.space(3));
    assertEquals("", Vba.space(0));
    try {
      String s = Vba.space(-2);
      fail("expected error, got " + s);
    } catch (RuntimeException e) {
      assertMessage(e, "NegativeArraySizeException");
    }
  }

  public void testString() {
    assertEquals("xxx", Vba.string(3, 'x'));
    assertEquals("", Vba.string(0, 'y'));
    try {
      String s = Vba.string(-2, 'z');
      fail("expected error, got " + s);
    } catch (RuntimeException e) {
      assertMessage(e, "NegativeArraySizeException");
    }
    assertEquals("", Vba.string(100, '\0'));
  }

  public void testStrReverse() {
    // odd length
    assertEquals("cba", Vba.strReverse("abc"));
    // even length
    assertEquals("wxyz", Vba.strReverse("zyxw"));
    // zero length
    assertEquals("", Vba.strReverse(""));
  }

  public void testTrim() {
    assertEquals("", Vba.trim(""));
    assertEquals("", Vba.trim("  "));
    assertEquals("abc", Vba.trim("abc"));
    assertEquals("abc", Vba.trim(" \n\tabc  \r"));
  }

  public void testWeekdayName() {
    // If Sunday (1) is the first day of the week
    // then day 1 is Sunday,
    // then day 2 is Monday,
    // and day 7 is Saturday
    assertEquals("Sunday", Vba.weekdayName(1, false, 1));
    assertEquals("Monday", Vba.weekdayName(2, false, 1));
    assertEquals("Saturday", Vba.weekdayName(7, false, 1));
    assertEquals("Sat", Vba.weekdayName(7, true, 1));

    // If Monday (2) is the first day of the week
    // then day 1 is Monday,
    // and day 7 is Sunday
    assertEquals("Monday", Vba.weekdayName(1, false, 2));
    assertEquals("Sunday", Vba.weekdayName(7, false, 2));

    // Use weekday start from locale. Test for the 2 most common.
    switch (Calendar.getInstance().getFirstDayOfWeek()) {
      case Calendar.SUNDAY:
        assertEquals("Sunday", Vba.weekdayName(1, false, 0));
        assertEquals("Monday", Vba.weekdayName(2, false, 0));
        assertEquals("Saturday", Vba.weekdayName(7, false, 0));
        assertEquals("Sat", Vba.weekdayName(7, true, 0));
        break;
      case Calendar.MONDAY:
        assertEquals("Monday", Vba.weekdayName(1, false, 0));
        assertEquals("Tuesday", Vba.weekdayName(2, false, 0));
        assertEquals("Sunday", Vba.weekdayName(7, false, 0));
        assertEquals("Sun", Vba.weekdayName(7, true, 0));
        break;
    }
  }

  // Mathematical

  public void testAbs() {
    assertEquals(Vba.abs(-1.7d), 1.7d);
  }

  public void testAtn() {
    assertEquals(0d, Vba.atn(0), SMALL);
    assertEquals(Math.PI / 4d, Vba.atn(1), SMALL);
  }

  public void testCos() {
    assertEquals(1d, Vba.cos(0), 0d);
    assertEquals(Vba.sqr(0.5d), Vba.cos(Math.PI / 4d), 0d);
    assertEquals(0d, Vba.cos(Math.PI / 2d), SMALL);
    assertEquals(-1d, Vba.cos(Math.PI), 0d);
  }

  public void testExp() {
    assertEquals(1d, Vba.exp(0));
    assertEquals(Math.E, Vba.exp(1), 1e-10);
  }

  public void testRound() {
    assertEquals(123d, Vba.round(123.4567d), SMALL);
  }

  public void testRound2() {
    assertEquals(123d, Vba.round(123.4567d, 0), SMALL);
    assertEquals(123.46d, Vba.round(123.4567d, 2), SMALL);
    assertEquals(120d, Vba.round(123.45d, -1), SMALL);
    assertEquals(-123.46d, Vba.round(-123.4567d, 2), SMALL);
  }

  public void testSgn() {
    assertEquals(1, Vba.sgn(3.11111d), 0d);
    assertEquals(-1, Vba.sgn(-Math.PI), 0d);
    assertTrue(0 == Vba.sgn(-0d));
    assertTrue(0 == Vba.sgn(0d));
  }

  public void testSin() {
    assertEquals(Vba.sqr(0.5d), Vba.sin(Math.PI / 4d), SMALL);
  }

  public void testSqr() {
    assertEquals(2d, Vba.sqr(4d), 0d);
    assertEquals(0d, Vba.sqr(0d), 0d);
    assertTrue(Double.isNaN(Vba.sqr(-4)));
  }

  public void testTan() {
    assertEquals(1d, Vba.tan(Math.PI / 4d), SMALL);
  }
}