Various utility classes moved to flash-utils.
git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5889 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -106,6 +106,7 @@
|
||||
<!-- compiler.library-path: list of SWC files or directories that contain SWC files-->
|
||||
<library-path>
|
||||
<path-element>../dist/lib/corelib.swc</path-element>
|
||||
<path-element>../dist/lib/ooolib.swc</path-element>
|
||||
</library-path>
|
||||
<!-- compiler.locale: specifies the locale for internationalization-->
|
||||
<locale>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<include name="samskivert.jar"/>
|
||||
<include name="swfutils-ooo.jar"/>
|
||||
<include name="velocity-1.5-dev.jar"/>
|
||||
<include name="ooolib*.swc"/>
|
||||
<include name="thane*.swc"/>
|
||||
<include name="corelib.swc"/>
|
||||
</fileset>
|
||||
|
||||
@@ -26,6 +26,7 @@ import flash.utils.Dictionary;
|
||||
|
||||
import com.threerings.util.ClassUtil;
|
||||
import com.threerings.util.Enum;
|
||||
import com.threerings.util.env.Environment;
|
||||
|
||||
import com.threerings.io.streamers.ArrayStreamer;
|
||||
import com.threerings.io.streamers.ByteStreamer;
|
||||
@@ -75,10 +76,10 @@ public class Streamer
|
||||
// But: the code is smaller, so that wins
|
||||
var clazz :Class = ClassUtil.getClassByName(Translations.getFromServer(jname));
|
||||
|
||||
if (ClassUtil.isAssignableAs(Enum, clazz)) {
|
||||
if (Environment.isAssignableAs(Enum, clazz)) {
|
||||
streamer = new EnumStreamer(clazz, jname);
|
||||
|
||||
} else if (ClassUtil.isAssignableAs(Streamable, clazz)) {
|
||||
} else if (Environment.isAssignableAs(Streamable, clazz)) {
|
||||
streamer = new Streamer(clazz, jname);
|
||||
|
||||
} else {
|
||||
|
||||
@@ -22,7 +22,9 @@
|
||||
package com.threerings.io.streamers {
|
||||
|
||||
import com.threerings.util.ClassUtil;
|
||||
import com.threerings.util.Enum;
|
||||
import com.threerings.util.Log;
|
||||
import com.threerings.util.env.Environment;
|
||||
|
||||
import com.threerings.io.ArrayMask;
|
||||
import com.threerings.io.ObjectInputStream;
|
||||
@@ -50,9 +52,8 @@ public class ArrayStreamer extends Streamer
|
||||
// form is "[L<class>;"
|
||||
var baseJClass :String = jname.substring(2, jname.length - 1);
|
||||
_delegate = Streamer.getStreamerByJavaName(baseJClass);
|
||||
_elementType = ClassUtil.getClassByName(
|
||||
Translations.getFromServer(baseJClass));
|
||||
_isFinal = ClassUtil.isFinal(_elementType);
|
||||
_elementType = ClassUtil.getClassByName(Translations.getFromServer(baseJClass));
|
||||
_isFinal = isFinal(_elementType);
|
||||
|
||||
} else if (secondChar === "I") {
|
||||
_elementType = int;
|
||||
@@ -148,6 +149,23 @@ public class ArrayStreamer extends Streamer
|
||||
}
|
||||
}
|
||||
|
||||
protected static function isFinal (type :Class) :Boolean
|
||||
{
|
||||
if (type === String) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// all enums are final, even if you forget to make your enum class final, you punk
|
||||
if (Environment.isAssignableAs(Enum, type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: there's currently no way to determine final from the class
|
||||
// I thought examining the prototype might do it, but no dice.
|
||||
// Fuckers!
|
||||
return false;
|
||||
}
|
||||
|
||||
/** A streamer for our elements. */
|
||||
protected var _delegate :Streamer;
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
/**
|
||||
* Provides a generic iterator for an Array.
|
||||
* No co-modification checking is done.
|
||||
*/
|
||||
public class ArrayIterator
|
||||
implements Iterator
|
||||
{
|
||||
/**
|
||||
* Create an ArrayIterator.
|
||||
*/
|
||||
public function ArrayIterator (arr :Array, allowRemove :Boolean = true)
|
||||
{
|
||||
_arr = arr;
|
||||
_index = 0;
|
||||
_lastIndex = allowRemove ? -1 : -2;
|
||||
}
|
||||
|
||||
// documentation inherited from interface Iterator
|
||||
public function hasNext () :Boolean
|
||||
{
|
||||
return (_index < _arr.length);
|
||||
}
|
||||
|
||||
// documentation inherited from interface Iterator
|
||||
public function next () :Object
|
||||
{
|
||||
if (_lastIndex != -2) {
|
||||
_lastIndex = _index;
|
||||
}
|
||||
return _arr[_index++];
|
||||
}
|
||||
|
||||
// documentation inherited from interface Iterator
|
||||
public function remove () :void
|
||||
{
|
||||
if (_lastIndex < 0) {
|
||||
throw new IllegalOperationError();
|
||||
|
||||
} else {
|
||||
_arr.splice(_lastIndex, 1);
|
||||
_lastIndex = -1;
|
||||
_index--; // since _lastIndex is always before _index
|
||||
}
|
||||
}
|
||||
|
||||
/** The array we're iterating over. */
|
||||
protected var _arr :Array;
|
||||
|
||||
/** The current index. */
|
||||
protected var _index :int;
|
||||
|
||||
/** The last-removed index.
|
||||
* Or -1 for already removed, -2 for no removals allowed. */
|
||||
protected var _lastIndex :int;
|
||||
}
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Contains methods that should be in Array, but aren't. Additionally
|
||||
* contains methods that understand the interfaces in this package.
|
||||
* So, for example, removeFirst() understands Equalable and will remove
|
||||
* an element that is equals() to the specified element, rather than just
|
||||
* === (strictly equals) to the specified element.
|
||||
*/
|
||||
public class ArrayUtil
|
||||
{
|
||||
/**
|
||||
* Creates a new Array and fills it with a default value.
|
||||
* @param size the size of the array
|
||||
* @param val the value to store at each index of the Array
|
||||
*/
|
||||
public static function create (size :uint, val :* = null) :Array
|
||||
{
|
||||
var arr :Array = new Array(size);
|
||||
for (var ii :uint = 0; ii < size; ii++) {
|
||||
arr[ii] = val;
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shallow copy of the array.
|
||||
*
|
||||
* @internal TODO: add support for copy ranges and deep copies?
|
||||
*/
|
||||
public static function copyOf (arr :Array) :Array
|
||||
{
|
||||
return arr.slice();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the specified array according to natural order- all elements
|
||||
* must implement Comparable or be null.
|
||||
*/
|
||||
public static function sort (arr :Array) :void
|
||||
{
|
||||
arr.sort(Comparators.COMPARABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a stable sort on the specified array.
|
||||
* @param comp a function that takes two objects in the array and returns -1 if the first
|
||||
* object should appear before the second in the container, 1 if it should appear after,
|
||||
* and 0 if the order does not matter. If omitted, Comparators.COMPARABLE is used and
|
||||
* the array elements should be Comparable objects.
|
||||
*/
|
||||
public static function stableSort (arr :Array, comp :Function = null) :void
|
||||
{
|
||||
if (comp == null) {
|
||||
comp = Comparators.COMPARABLE;
|
||||
}
|
||||
// insertion sort implementation
|
||||
var nn :int = arr.length;
|
||||
for (var ii :int = 1; ii < nn; ii++) {
|
||||
var val :* = arr[ii];
|
||||
var jj :int = ii - 1;
|
||||
for (; jj >= 0; jj--) {
|
||||
var compVal :* = arr[jj];
|
||||
if (comp(val, compVal) >= 0) {
|
||||
break;
|
||||
}
|
||||
arr[jj + 1] = compVal;
|
||||
}
|
||||
arr[jj + 1] = val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts an object into a sorted Array in its correct, sorted location.
|
||||
*
|
||||
* @param comp a function that takes two objects in the array and returns -1 if the first
|
||||
* object should appear before the second in the container, 1 if it should appear after,
|
||||
* and 0 if the order does not matter. If omitted, Comparators.COMPARABLE is used and
|
||||
* the array elements should be Comparable objects.
|
||||
*
|
||||
* @return the index of the inserted item
|
||||
*/
|
||||
public static function sortedInsert (arr :Array, val :*, comp :Function = null) :int
|
||||
{
|
||||
if (comp == null) {
|
||||
comp = Comparators.COMPARABLE;
|
||||
}
|
||||
|
||||
var insertedIdx :int = -1;
|
||||
var nn :int = arr.length;
|
||||
for (var ii :int = 0; ii < nn; ii++) {
|
||||
var compVal :* = arr[ii];
|
||||
if (comp(val, compVal) <= 0) {
|
||||
arr.splice(ii, 0, val);
|
||||
insertedIdx = ii;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (insertedIdx < 0) {
|
||||
arr.push(val);
|
||||
insertedIdx = arr.length - 1;
|
||||
}
|
||||
|
||||
return insertedIdx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Randomly shuffle the elements in the specified array.
|
||||
*
|
||||
* @param rando a random number generator to use, or null if you don't care.
|
||||
*/
|
||||
public static function shuffle (arr :Array, rando :Random = null) :void
|
||||
{
|
||||
var randFunc :Function = (rando != null) ? rando.nextInt :
|
||||
function (n :int) :int {
|
||||
return int(Math.random() * n);
|
||||
};
|
||||
// starting from the end of the list, repeatedly swap the element in
|
||||
// question with a random element previous to it up to and including
|
||||
// itself
|
||||
for (var ii :int = arr.length - 1; ii > 0; ii--) {
|
||||
var idx :int = randFunc(ii + 1);
|
||||
var tmp :Object = arr[idx];
|
||||
arr[idx] = arr[ii];
|
||||
arr[ii] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the first item in the array for which the predicate function
|
||||
* returns true, or -1 if no such item was found. The predicate function should be of type:
|
||||
* function (element :*) :Boolean { }
|
||||
*
|
||||
* @return the zero-based index of the matching element, or -1 if none found.
|
||||
*/
|
||||
public static function indexIf (arr :Array, predicate :Function) :int
|
||||
{
|
||||
if (arr != null) {
|
||||
for (var ii :int = 0; ii < arr.length; ii++) {
|
||||
if (predicate(arr[ii])) {
|
||||
return ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1; // never found
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first item in the array for which the predicate function returns true, or
|
||||
* undefined if no such item was found. The predicate function should be of type:
|
||||
* function (element :*) :Boolean { }
|
||||
*
|
||||
* @return the matching element, or undefined if no matching element was found.
|
||||
*/
|
||||
public static function findIf (arr :Array, predicate :Function) :*
|
||||
{
|
||||
var index :int = (arr != null ? indexIf(arr, predicate) : -1);
|
||||
return (index >= 0 ? arr[index] : undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first index of the supplied element in the array. Note that if the element
|
||||
* implements Equalable, an element that is equals() will have its index returned, instead
|
||||
* of requiring the search element to be === (strictly equal) to an element in the array
|
||||
* like Array.indexOf().
|
||||
*
|
||||
* @return the zero-based index of the matching element, or -1 if none found.
|
||||
*/
|
||||
public static function indexOf (arr :Array, element :Object) :int
|
||||
{
|
||||
if (arr != null) {
|
||||
for (var ii :int = 0; ii < arr.length; ii++) {
|
||||
if (Util.equals(arr[ii], element)) {
|
||||
return ii;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1; // never found
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the specified element, or one that is Equalable.equals() to it, is
|
||||
* contained in the array.
|
||||
*/
|
||||
public static function contains (arr :Array, element :Object) :Boolean
|
||||
{
|
||||
return (indexOf(arr, element) != -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first instance of the specified element from the array.
|
||||
*
|
||||
* @return true if an element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeFirst (arr :Array, element :Object) :Boolean
|
||||
{
|
||||
return removeImpl(arr, element, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the last instance of the specified element from the array.
|
||||
*
|
||||
* @return true if an element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeLast (arr :Array, element :Object) :Boolean
|
||||
{
|
||||
arr.reverse();
|
||||
var removed :Boolean = removeFirst(arr, element);
|
||||
arr.reverse();
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all instances of the specified element from the array.
|
||||
*
|
||||
* @return true if at least one element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeAll (arr :Array, element :Object) :Boolean
|
||||
{
|
||||
return removeImpl(arr, element, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element in the array for which the specified predicate returns true.
|
||||
*
|
||||
* @param pred a Function of the form: function (element :*) :Boolean
|
||||
*
|
||||
* @return true if an element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeFirstIf (arr :Array, pred :Function) :Boolean
|
||||
{
|
||||
return removeIfImpl(arr, pred, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last element in the array for which the specified predicate returns true.
|
||||
*
|
||||
* @param pred a Function of the form: function (element :*) :Boolean
|
||||
*
|
||||
* @return true if an element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeLastIf (arr :Array, pred :Function) :Boolean
|
||||
{
|
||||
arr.reverse();
|
||||
var removed :Boolean = removeFirstIf(arr, pred);
|
||||
arr.reverse();
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all elements in the array for which the specified predicate returns true.
|
||||
*
|
||||
* @param pred a Function of the form: function (element :*) :Boolean
|
||||
*
|
||||
* @return true if an element was removed, false otherwise.
|
||||
*/
|
||||
public static function removeAllIf (arr :Array, pred :Function) :Boolean
|
||||
{
|
||||
return removeIfImpl(arr, pred, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the two arrays contain elements that are all equals()?
|
||||
*/
|
||||
public static function equals (ar1 :Array, ar2 :Array) :Boolean
|
||||
{
|
||||
if (ar1 === ar2) {
|
||||
return true;
|
||||
|
||||
} else if (ar1 == null || ar2 == null || ar1.length != ar2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var jj :int = 0; jj < ar1.length; jj++) {
|
||||
if (!Util.equals(ar1[jj], ar2[jj])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a segment of one array to another.
|
||||
* @param src the array to copy from
|
||||
* @param srcoffset the position in the source array to begin copying from
|
||||
* @param dst the array to copy into
|
||||
* @param dstoffset the position in the destition array to begin copying into
|
||||
* @param count the number of elements to copy
|
||||
*/
|
||||
public static function copy (
|
||||
src :Array, srcoffset :uint, dst :Array, dstoffset :uint, count :uint) :void
|
||||
{
|
||||
for (var ii :uint = 0; ii < count; ++ii) {
|
||||
dst[dstoffset++] = src[srcoffset++];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of remove methods.
|
||||
*/
|
||||
private static function removeImpl (
|
||||
arr :Array, element :Object, firstOnly :Boolean) :Boolean
|
||||
{
|
||||
return removeIfImpl(arr, createEqualsPred(element), firstOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of removeIf methods.
|
||||
*/
|
||||
private static function removeIfImpl (arr :Array, pred :Function, firstOnly :Boolean) :Boolean
|
||||
{
|
||||
var removed :Boolean = false;
|
||||
for (var ii :int = 0; ii < arr.length; ii++) {
|
||||
if (pred(arr[ii])) {
|
||||
arr.splice(ii--, 1);
|
||||
if (firstOnly) {
|
||||
return true;
|
||||
}
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static function createEqualsPred (element :Object) :Function
|
||||
{
|
||||
return function (other :Object) :Boolean {
|
||||
return Util.equals(other, element);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.system.Capabilities;
|
||||
import flash.system.System;
|
||||
|
||||
/**
|
||||
* Simple implementation of assertion checks for debug environments.
|
||||
* When running in a debug player, each function will test the assert expression,
|
||||
* and if it fails, log an error message with a stack trace. When running in a
|
||||
* release player, these functions do not run any tests, and exit immediately.
|
||||
*
|
||||
* Note: stack dumping is controlled via the Assert.dumpStack parameter.
|
||||
*
|
||||
* Usage example:
|
||||
* <pre>
|
||||
* Assert.isNotNull(mystack.top());
|
||||
* Assert.isTrue(mystack.length == 1, "Unexpected number of items on stack!");
|
||||
* </pre>
|
||||
*/
|
||||
public class Assert
|
||||
{
|
||||
/** Controls whether stack dumps should be included in the error log (default value is true).*/
|
||||
public static var dumpStack :Boolean = true;
|
||||
|
||||
/** Asserts that the value is equal to null. */
|
||||
public static function isNull (value :Object, message :String = null) :void
|
||||
{
|
||||
if (_debug && (value != null)) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Asserts that the value is not equal to null. */
|
||||
public static function isNotNull (value :Object, message :String = null) :void
|
||||
{
|
||||
if (_debug && (value == null)) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Asserts that the value is false. */
|
||||
public static function isFalse (value :Boolean, message :String = null) :void
|
||||
{
|
||||
if (_debug && value) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Asserts that the value is true. */
|
||||
public static function isTrue (value :Boolean, message :String = null) :void
|
||||
{
|
||||
if (_debug && ! value) {
|
||||
fail(message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Displays an error message, with an optional stack trace. */
|
||||
public static function fail (message :String) :void
|
||||
{
|
||||
_log.warning("Failure" + ((message != null) ? (": " + message) : ""));
|
||||
if (dumpStack) {
|
||||
_log.warning(new Error("dumpStack").getStackTrace());
|
||||
}
|
||||
|
||||
// try to exit, but don't booch if we're running in an older player
|
||||
var o :Object = System;
|
||||
try {
|
||||
o["exit"](1); // call System.exit(1);
|
||||
} catch (err :SecurityError) {
|
||||
// probably not allowed
|
||||
}
|
||||
}
|
||||
|
||||
protected static var _log :Log = Log.getLog(Assert);
|
||||
protected static var _debug :Boolean = Capabilities.isDebugger;
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* An enum value that can be persisted as a byte.
|
||||
*/
|
||||
public interface ByteEnum
|
||||
{
|
||||
/**
|
||||
* Return the byte form of this enum.
|
||||
*/
|
||||
function toByte () :int;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* ByteEnum utility methods.
|
||||
*/
|
||||
public class ByteEnumUtil
|
||||
{
|
||||
/**
|
||||
* Returns the enum value with the specified code in the supplied enum class.
|
||||
* Throws ArgumentError if the enum lacks a value that maps to the supplied code.
|
||||
*/
|
||||
public static function fromByte (clazz :Class, code :int) :Enum
|
||||
{
|
||||
// we could do something fancier than this O(n) impl, in the future...
|
||||
for each (var e :Enum in Enum.values(clazz)) {
|
||||
if (ByteEnum(e).toByte() == code) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
throw new ArgumentError(clazz + " has no value with code " + code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.describeType;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
import flash.utils.getDefinitionByName;
|
||||
|
||||
import com.threerings.util.env.Environment;
|
||||
|
||||
public class ClassUtil
|
||||
{
|
||||
/**
|
||||
* Get the full class name, e.g. "com.threerings.util.ClassUtil".
|
||||
* Calling getClassName with a Class object will return the same value as calling it with an
|
||||
* instance of that class. That is, getClassName(Foo) == getClassName(new Foo()).
|
||||
*/
|
||||
public static function getClassName (obj :Object) :String
|
||||
{
|
||||
return getQualifiedClassName(obj).replace("::", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class name with the last part of the package, e.g. "util.ClassUtil".
|
||||
*/
|
||||
public static function shortClassName (obj :Object) :String
|
||||
{
|
||||
var s :String = getQualifiedClassName(obj);
|
||||
var dex :int = s.lastIndexOf(".");
|
||||
s = s.substring(dex + 1); // works even if dex is -1
|
||||
return s.replace("::", ".");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get just the class name, e.g. "ClassUtil".
|
||||
*/
|
||||
public static function tinyClassName (obj :Object) :String
|
||||
{
|
||||
var s :String = getClassName(obj);
|
||||
var dex :int = s.lastIndexOf(".");
|
||||
return s.substring(dex + 1); // works even if dex is -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new instance that is the same class as the specified
|
||||
* object. The class must have a zero-arg constructor.
|
||||
*/
|
||||
public static function newInstance (obj :Object) :Object
|
||||
{
|
||||
var clazz :Class = getClass(obj);
|
||||
return new clazz();
|
||||
}
|
||||
|
||||
public static function isSameClass (obj1 :Object, obj2 :Object) :Boolean
|
||||
{
|
||||
return (getQualifiedClassName(obj1) == getQualifiedClassName(obj2));
|
||||
}
|
||||
|
||||
public static function getClass (obj :Object) :Class
|
||||
{
|
||||
if (obj.constructor is Class) {
|
||||
return Class(obj.constructor);
|
||||
}
|
||||
return getClassByName(getQualifiedClassName(obj));
|
||||
}
|
||||
|
||||
public static function getClassByName (cname :String) :Class
|
||||
{
|
||||
try {
|
||||
return (getDefinitionByName(cname.replace("::", ".")) as Class);
|
||||
|
||||
} catch (error :ReferenceError) {
|
||||
var log :Log = Log.getLog(ClassUtil);
|
||||
log.warning("Unknown class: " + cname);
|
||||
log.logStackTrace(error);
|
||||
}
|
||||
return null; // error case
|
||||
}
|
||||
|
||||
public static function isFinal (type :Class) :Boolean
|
||||
{
|
||||
if (type === String) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// all enums are final, even if you forget to make your enum class final, you punk
|
||||
if (isAssignableAs(Enum, type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: there's currently no way to determine final from the class
|
||||
// I thought examining the prototype might do it, but no dice.
|
||||
// Fuckers!
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an object of type srcClass is a subclass of or
|
||||
* implements the interface represented by the asClass paramter.
|
||||
*
|
||||
* <code>
|
||||
* if (ClassUtil.isAssignableAs(Streamable, someClass)) {
|
||||
* var s :Streamable = (new someClass() as Streamable);
|
||||
* </code>
|
||||
*/
|
||||
public static function isAssignableAs (asClass :Class, srcClass :Class) :Boolean
|
||||
{
|
||||
return Environment.isAssignableAs(asClass, srcClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public interface Cloneable
|
||||
{
|
||||
/**
|
||||
* Create a clone of this object.
|
||||
*/
|
||||
function clone () :Object;
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.IEventDispatcher;
|
||||
|
||||
/**
|
||||
* Contains a simple binding function to bind events to commands.
|
||||
*
|
||||
* TODO: be able to set up a Command object, and bind it to multiple functions
|
||||
*/
|
||||
public class Command
|
||||
{
|
||||
/**
|
||||
* Bind an event to a command.
|
||||
*/
|
||||
public static function bind (
|
||||
source :IEventDispatcher, eventType :String, cmdOrFn :Object, arg :Object = null) :void
|
||||
{
|
||||
source.addEventListener(eventType, function (... ignored) :void {
|
||||
CommandEvent.dispatch(source, cmdOrFn, arg);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience, since otherwise nobody usually needs to ever import CommandEvent.
|
||||
*/
|
||||
public static function dispatch (
|
||||
source :IEventDispatcher, cmdOrFn :Object, arg :Object = null) :void
|
||||
{
|
||||
CommandEvent.dispatch(source, cmdOrFn, arg);
|
||||
}
|
||||
|
||||
// public static function bind (
|
||||
// source :IEventDispatcher, eventType :String, cmdOrFn :Object, arg :Object = null) :Command
|
||||
// {
|
||||
// var cmd :Command = new Command(cmdOrFn, arg);
|
||||
// cmd.addBind(source, eventType);
|
||||
// return cmd;
|
||||
// }
|
||||
//
|
||||
// public function Command (cmdOrFn :Object, arg :Object = null)
|
||||
// {
|
||||
// _cmdOrFn = cmdOrFn;
|
||||
// _arg = arg;
|
||||
// }
|
||||
//
|
||||
// public function setOverrideSource (disp :IEventDispatcher) :void
|
||||
// {
|
||||
// _source = source;
|
||||
// }
|
||||
//
|
||||
// public function addBind (source :IEventDispatcher, eventType :String) :void
|
||||
// {
|
||||
// source.addEventListener(eventType, eventHandler);
|
||||
// }
|
||||
//
|
||||
// public function removeBind (source :IEventDispatcher, eventType :String) :void
|
||||
// {
|
||||
// source.removeEventListener(eventType, eventHandler);
|
||||
// }
|
||||
//
|
||||
// protected function eventHandler (event :Event) :void
|
||||
// {
|
||||
// dispatch(_source || IEventDispatcher(event.source), _cmdOrFn, arg);
|
||||
// }
|
||||
//
|
||||
// /** An override source for the command events, or null to use the triggering dispatcher. */
|
||||
// protected var _source :IEventDispatcher;
|
||||
// protected var _cmdOrFn :Object;
|
||||
// protected var _arg :Object;
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.IEventDispatcher;
|
||||
|
||||
public class CommandEvent extends Event
|
||||
{
|
||||
/** The event type for all controller events. */
|
||||
public static const COMMAND :String = "commandEvt";
|
||||
|
||||
/**
|
||||
* Use this method to dispatch CommandEvents.
|
||||
*/
|
||||
public static function dispatch (
|
||||
disp :IEventDispatcher, cmdOrFn :Object, arg :Object = null) :void
|
||||
{
|
||||
if (cmdOrFn is Function) {
|
||||
var fn :Function = (cmdOrFn as Function);
|
||||
// build our args array
|
||||
var args :Array;
|
||||
if (arg == null) {
|
||||
args = null;
|
||||
|
||||
} else if (arg is Array) {
|
||||
// if we were passed an array, treat it as the arg array.
|
||||
// Note: if you want to pass a single array param, you've
|
||||
// got to wrap it in another array, so sorry.
|
||||
args = arg as Array;
|
||||
|
||||
} else {
|
||||
args = [ arg ];
|
||||
}
|
||||
|
||||
// now trying calling it
|
||||
try {
|
||||
// TODO: Tim Conkling has determined that fn.length will tell you the required
|
||||
// number of args to a function. We may be able to do something smarter here
|
||||
// with that, but I'll wait until it's a problem.
|
||||
fn.apply(null, args);
|
||||
} catch (err :Error) {
|
||||
Log.getLog(CommandEvent).warning("Unable to call callback.", err);
|
||||
}
|
||||
|
||||
} else if (cmdOrFn is String) {
|
||||
var cmd :String = String(cmdOrFn);
|
||||
// Create the event to dispatch
|
||||
var event :CommandEvent = create(cmd, arg);
|
||||
|
||||
// Dispatch it. A return value of true means that the event was
|
||||
// never cancelled, so we complain.
|
||||
if (disp == null || disp.dispatchEvent(event)) {
|
||||
Log.getLog(CommandEvent).warning("Unhandled controller command",
|
||||
"cmd", cmd, "arg", arg, "disp", disp);
|
||||
}
|
||||
|
||||
} else {
|
||||
throw new ArgumentError("Argument 'cmdOrFn' must be a command (String) or a Function");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a bridge from something like a pop-up window to an alternate target.
|
||||
*/
|
||||
public static function configureBridge (
|
||||
source :IEventDispatcher, target :IEventDispatcher) :void
|
||||
{
|
||||
source.addEventListener(COMMAND,
|
||||
function (event :CommandEvent) :void {
|
||||
event.markAsHandled();
|
||||
dispatch(target, event.command, event.arg);
|
||||
},
|
||||
false, -1);
|
||||
}
|
||||
|
||||
/** The command. */
|
||||
public var command :String;
|
||||
|
||||
/** An optional argument. */
|
||||
public var arg :Object;
|
||||
|
||||
/**
|
||||
* Command events may not be directly constructed, use the dispatch
|
||||
* method to do your work.
|
||||
*/
|
||||
public function CommandEvent (command :String, arg :Object)
|
||||
{
|
||||
super(COMMAND, true, true);
|
||||
if (_blockConstructor) {
|
||||
throw new IllegalOperationError();
|
||||
}
|
||||
this.command = command;
|
||||
this.arg = arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this command as handled, stopping its propagation up the
|
||||
* hierarchy.
|
||||
*/
|
||||
public function markAsHandled () :void
|
||||
{
|
||||
preventDefault();
|
||||
stopImmediatePropagation();
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return create(command, arg);
|
||||
}
|
||||
|
||||
override public function toString () :String
|
||||
{
|
||||
return "CommandEvent[" + command + " (" + arg + ")]";
|
||||
}
|
||||
|
||||
/**
|
||||
* A factory method for privately creating command events.
|
||||
*/
|
||||
protected static function create (cmd :String, arg :Object) :CommandEvent
|
||||
{
|
||||
var event :CommandEvent;
|
||||
_blockConstructor = false;
|
||||
try {
|
||||
event = new CommandEvent(cmd, arg);
|
||||
} finally {
|
||||
_blockConstructor = true;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
/** Used to prevent unauthorized construction. */
|
||||
protected static var _blockConstructor :Boolean = true;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public interface Comparable
|
||||
{
|
||||
/**
|
||||
* Compare this object to the other one, and return 0 if they're equal,
|
||||
* -1 if this object is less than the other, or 1 if this object is greater.
|
||||
* Note: Please use [-1, 0, 1] to be compatible with flex Sort objects.
|
||||
*/
|
||||
function compareTo (other :Object) :int;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Contains sorting Comparators.
|
||||
* These functions are suitable for passing to Array.sort(), or with a flex Sort object.
|
||||
*/
|
||||
public class Comparators
|
||||
{
|
||||
/**
|
||||
* A standard Comparator for comparing Comparable values.
|
||||
*/
|
||||
public static function COMPARABLE (c1 :Comparable, c2 :Comparable, ... ignored) :int
|
||||
{
|
||||
if (c1 == c2) { // same object -or- both null
|
||||
return 0;
|
||||
} else if (c1 == null) {
|
||||
return -1;
|
||||
} else if (c2 == null) {
|
||||
return 1;
|
||||
} else {
|
||||
return c1.compareTo(c2);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Comparator function that reverses the ordering of the specified Comparator.
|
||||
*/
|
||||
public static function createReverse (comparator :Function) :Function
|
||||
{
|
||||
return function (o1 :Object, o2 :Object, ... ignored) :int {
|
||||
return comparator(o2, o1); // simply reverse the ordering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.NetStatusEvent;
|
||||
import flash.events.EventDispatcher;
|
||||
|
||||
import flash.net.SharedObject;
|
||||
import flash.net.SharedObjectFlushStatus;
|
||||
|
||||
/**
|
||||
* Dispatched when this Config object has a value set on it.
|
||||
*
|
||||
* @eventType com.threerings.util.ConfigValueSetEvent.CONFIG_VALUE_SET
|
||||
*/
|
||||
[Event(name="ConfigValSet", type="com.threerings.util.ConfigValueSetEvent")]
|
||||
|
||||
public class Config extends EventDispatcher
|
||||
{
|
||||
/**
|
||||
* Constructs a new config object which will obtain configuration
|
||||
* information from the specified path.
|
||||
*/
|
||||
public function Config (path :String)
|
||||
{
|
||||
setPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path, if null then we aren't persisting settings.
|
||||
*/
|
||||
public function setPath (path :String) :void
|
||||
{
|
||||
_so = (path == null) ? null : SharedObject.getLocal("config_" + path, "/");
|
||||
_data = (_so == null) ? {} : _so.data;
|
||||
|
||||
// dispatch events for all settings
|
||||
for (var n :String in _data) {
|
||||
dispatchEvent(new ConfigValueSetEvent(n, _data[n]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Are we persisting settings?
|
||||
*/
|
||||
public function isPersisting () :Boolean
|
||||
{
|
||||
return (_so != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches and returns the value for the specified configuration property.
|
||||
*/
|
||||
public function getValue (name :String, defValue :Object) :Object
|
||||
{
|
||||
var val :* = _data[name];
|
||||
return (val === undefined) ? defValue : val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value specified.
|
||||
*/
|
||||
public function setValue (name :String, value :Object, flush :Boolean = true) :void
|
||||
{
|
||||
_data[name] = value;
|
||||
if (flush && _so != null) {
|
||||
_so.flush(); // flushing is not strictly necessary
|
||||
}
|
||||
|
||||
// dispatch an event corresponding
|
||||
dispatchEvent(new ConfigValueSetEvent(name, value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove any set value for the specified preference.
|
||||
* This does not dispatch an event because this would only be done to remove an
|
||||
* obsolete preference.
|
||||
*/
|
||||
public function remove (name :String) :void
|
||||
{
|
||||
delete _data[name];
|
||||
if (_so != null) {
|
||||
_so.flush(); // flushing is not strictly necessary
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that we can store preferences up to the specified size.
|
||||
* Note that calling this method may pop up a dialog to the user, asking
|
||||
* them if it's ok to increase the capacity. The result listener may
|
||||
* never be called if the user doesn't answer the pop-up.
|
||||
*
|
||||
* @param rl an optional listener that will be informed as to whether
|
||||
* the request succeeded.
|
||||
*/
|
||||
public function ensureCapacity (kilobytes :int, rl :ResultListener) :void
|
||||
{
|
||||
if (_so == null) {
|
||||
if (rl != null) {
|
||||
rl.requestCompleted(this);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// flush with the size, see if we're cool
|
||||
var result :String = _so.flush(1024 * kilobytes);
|
||||
if (rl == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// success
|
||||
if (result == SharedObjectFlushStatus.FLUSHED) {
|
||||
rl.requestCompleted(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise we'll hear back in a sec
|
||||
var thisConfig :Config = this;
|
||||
var listener :Function = function (evt :NetStatusEvent) :void {
|
||||
// TODO: There is a bug where the status is always
|
||||
// "SharedObject.Flush.Failed", even on success, if the request
|
||||
// was for a large enough storage that the player calls it
|
||||
// "unlimited".
|
||||
trace("================[" + evt.info.code + "]");
|
||||
if ("SharedObject.Flush.Success" == evt.info.code) {
|
||||
rl.requestCompleted(thisConfig);
|
||||
} else {
|
||||
rl.requestFailed(new Error(String(evt.info.code)));
|
||||
}
|
||||
_so.removeEventListener(NetStatusEvent.NET_STATUS, listener);
|
||||
};
|
||||
|
||||
_so.addEventListener(NetStatusEvent.NET_STATUS, listener);
|
||||
}
|
||||
|
||||
/** The shared object that contains our preferences. */
|
||||
protected var _so :SharedObject;
|
||||
|
||||
/** The object in which we store things, usually _so.data. */
|
||||
protected var _data :Object;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* Dispatched whenever a Config value is changed.
|
||||
*/
|
||||
public class ConfigValueSetEvent extends Event
|
||||
{
|
||||
/** The type of a ConfigValueSetEvent. */
|
||||
public static const CONFIG_VALUE_SET :String = "ConfigValSet";
|
||||
|
||||
/** The name of the config value set. */
|
||||
public var name :String;
|
||||
|
||||
/** The new value. */
|
||||
public var value :Object;
|
||||
|
||||
/**
|
||||
*/
|
||||
public function ConfigValueSetEvent (name :String, value :Object)
|
||||
{
|
||||
super(CONFIG_VALUE_SET);
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return new ConfigValueSetEvent(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.IEventDispatcher;
|
||||
|
||||
public class Controller
|
||||
{
|
||||
/**
|
||||
* Set the panel being controlled.
|
||||
*/
|
||||
protected function setControlledPanel (panel :IEventDispatcher) :void
|
||||
{
|
||||
if (_controlledPanel != null) {
|
||||
_controlledPanel.removeEventListener(
|
||||
CommandEvent.COMMAND, handleCommandEvent);
|
||||
}
|
||||
_controlledPanel = panel;
|
||||
if (_controlledPanel != null) {
|
||||
_controlledPanel.addEventListener(
|
||||
CommandEvent.COMMAND, handleCommandEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an action so that it can be handled by this controller or
|
||||
* another controller above it in the display list.
|
||||
*/
|
||||
public final function postAction (cmd :String, arg :Object = null) :void
|
||||
{
|
||||
CommandEvent.dispatch(_controlledPanel, cmd, arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an action that was generated by our panel or some child.
|
||||
*
|
||||
* @return true if the specified action was handled, false otherwise.
|
||||
*
|
||||
* When creating your own controller, override this function and return
|
||||
* true for any command handled, and call super for any unknown commands.
|
||||
*/
|
||||
public function handleAction (cmd :String, arg :Object) :Boolean
|
||||
{
|
||||
// fall back to a method named the cmd
|
||||
var fn :Function = null;
|
||||
try {
|
||||
fn = (this[cmd] as Function);
|
||||
} catch (e :Error) {
|
||||
// suppress
|
||||
//Log.testing("Caught error finding '" + cmd + "()' [" + this + "]");
|
||||
}
|
||||
if (fn == null) {
|
||||
// try the old style with "handle" prepended
|
||||
try {
|
||||
fn = (this["handle" + cmd] as Function);
|
||||
} catch (e :Error) {
|
||||
// suppress
|
||||
//Log.testing("Caught error finding 'handle" + cmd + "()' [" +
|
||||
// this + "]");
|
||||
}
|
||||
}
|
||||
if (fn == null) {
|
||||
// never found it?
|
||||
return false;
|
||||
}
|
||||
|
||||
// finally, dispatch it
|
||||
CommandEvent.dispatch(_controlledPanel, fn, arg);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private function to handle the controller event and call
|
||||
* handleAction.
|
||||
*/
|
||||
private function handleCommandEvent (event :CommandEvent) :void
|
||||
{
|
||||
if (handleAction(event.command, event.arg)) {
|
||||
// if we handle the event, stop it from moving outward to another
|
||||
// controller
|
||||
event.markAsHandled();
|
||||
}
|
||||
}
|
||||
|
||||
/** The panel currently being controlled. */
|
||||
protected var _controlledPanel :IEventDispatcher;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Utility for dates.
|
||||
*/
|
||||
public class DateUtil
|
||||
{
|
||||
/**
|
||||
* Calculates a brief, conversational representation of the given date relative to 'now':
|
||||
*
|
||||
* Date occured in a past year:
|
||||
* 10/10/1969
|
||||
* Date occured over 6 days ago:
|
||||
* Oct 10
|
||||
* Date occured over 23 hours ago:
|
||||
* Wed 15:10
|
||||
* Date occured in the past 23 hours:
|
||||
* 15:10
|
||||
**/
|
||||
public static function getConversationalDateString (date :Date, now :Date = null) :String
|
||||
{
|
||||
if (now == null) {
|
||||
now = new Date();
|
||||
}
|
||||
if (date.fullYear != now.fullYear) {
|
||||
// e.g. 25/10/06
|
||||
return date.day + "/" + date.month + "/" + date.fullYear;
|
||||
}
|
||||
var hourDiff :uint = (now.time - date.time) / (3600 * 1000);
|
||||
if (hourDiff > 6*24) {
|
||||
// e.g. Oct 25
|
||||
return getMonthName(date.month) + " " + date.day;
|
||||
}
|
||||
if (hourDiff > 23) {
|
||||
// e.g. Wed 15:10
|
||||
return getDayName(date.day) + " " + date.hours + ":" +
|
||||
(date.minutes < 10 ? "0" : "") + date.minutes;
|
||||
}
|
||||
// e.g. 15:10
|
||||
return date.hours + ":" + (date.minutes < 10 ? "0" : "") + date.minutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the given (integer) month; 0 is January, and so on.
|
||||
*/
|
||||
public static function getMonthName (month :uint, full :Boolean = false) :String
|
||||
{
|
||||
return full ? _months[month] : _months[month].substr(0, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the given (integer) day; 0 is Sunday, and so on.
|
||||
*/
|
||||
public static function getDayName (day :uint, full :Boolean = false) :String
|
||||
{
|
||||
return full ? _days[day] : _days[day].substr(0, 3);
|
||||
}
|
||||
|
||||
protected static var _days :Array =
|
||||
[ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ];
|
||||
protected static var _months :Object =
|
||||
[ "January", "February", "March", "April", "May", "June",
|
||||
"July", "August", "September", "October", "November", "December" ];
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* Base enum class for actionscript. Works pretty much like enums in Java, only you've got
|
||||
* to do one or two things.
|
||||
*
|
||||
* To use, you'll want to subclass and have something like the following:
|
||||
*
|
||||
* public final class Foo extends Enum
|
||||
* {
|
||||
* public static const ONE :Foo = new Foo("ONE");
|
||||
* public static const TWO :Foo = new Foo("TWO");
|
||||
* finishedEnumerating(Foo);
|
||||
*
|
||||
* / ** {at}private * /
|
||||
* public function Foo (name :String)
|
||||
* {
|
||||
* super(name);
|
||||
* }
|
||||
*
|
||||
* public static function valueOf (name :String) :Foo
|
||||
* {
|
||||
* return Enum.valueOf(Foo, name) as Foo;
|
||||
* }
|
||||
*
|
||||
* public static function values () :Array
|
||||
* {
|
||||
* return Enum.values(Foo);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* Important notes:
|
||||
* - make your class final
|
||||
* - create a constructor that calls super(name)
|
||||
* - declare your enum constants const, and with the same String as their name.
|
||||
* - call finishedEnumerating() at the end of your constants.
|
||||
* - your enum objects should be immutable
|
||||
* - implement a static valueOf() and values() methods for extra points, as above.
|
||||
*/
|
||||
public class Enum
|
||||
implements Hashable, Comparable
|
||||
{
|
||||
/**
|
||||
* Call this constructor in your enum subclass constructor.
|
||||
*/
|
||||
public function Enum (name :String)
|
||||
{
|
||||
const clazz :Class = ClassUtil.getClass(this);
|
||||
if (Boolean(_blocked[clazz])) {
|
||||
throw new Error("You may not just construct an enum!");
|
||||
|
||||
} else if (name == null) {
|
||||
throw new ArgumentError("null is invalid.");
|
||||
}
|
||||
|
||||
var list :Array = _enums[clazz] as Array;
|
||||
if (list == null) {
|
||||
list = [];
|
||||
_enums[clazz] = list;
|
||||
} else {
|
||||
for each (var enum :Enum in list) {
|
||||
if (enum.name() === name) {
|
||||
throw new ArgumentError("Duplicate enum: " + name);
|
||||
}
|
||||
}
|
||||
}
|
||||
list.push(this);
|
||||
|
||||
// now, actually construct
|
||||
_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this enum.
|
||||
*/
|
||||
public function name () :String
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ordinal of this enum.
|
||||
* Note that you should not use the ordinal in normal cases, as it may change if a new
|
||||
* enum is defined. Ordinals should only be used if you are writing a data structure
|
||||
* that generically handles enums in an efficient manner, and you are never persisting
|
||||
* anything where the ordinal can change.
|
||||
*/
|
||||
public function ordinal () :int
|
||||
{
|
||||
return (_enums[ClassUtil.getClass(this)] as Array).indexOf(this);
|
||||
}
|
||||
|
||||
// from Hashable
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
// enums are singleton
|
||||
return (other === this);
|
||||
}
|
||||
|
||||
// from Hashable
|
||||
public function hashCode () :int
|
||||
{
|
||||
return ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the String representation of this enum.
|
||||
*/
|
||||
public function toString () :String
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
// from Comparable
|
||||
public function compareTo (other :Object) :int
|
||||
{
|
||||
if (!ClassUtil.isSameClass(this, other)) {
|
||||
throw new ArgumentError("Not same class");
|
||||
}
|
||||
return Integer.compare(this.ordinal(), Enum(other).ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a String name into an Enum constant.
|
||||
*/
|
||||
public static function valueOf (clazz :Class, name :String) :Enum
|
||||
{
|
||||
for each (var enum :Enum in values(clazz)) {
|
||||
if (enum.name() === name) {
|
||||
return enum;
|
||||
}
|
||||
}
|
||||
throw new ArgumentError("No such enum [class=" + clazz + ", name=" + name + "].");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the enums of the specified class, or null if it's not an enum.
|
||||
*/
|
||||
public static function values (clazz :Class) :Array
|
||||
{
|
||||
var arr :Array = _enums[clazz] as Array;
|
||||
if (arr == null) {
|
||||
throw new ArgumentError("Not an enum [class=" + clazz + "].");
|
||||
}
|
||||
return arr.concat(); // return a copy, so that callers may not fuxor
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called by your enum subclass after you've finished enumating the enum
|
||||
* constants. See the example in the class header documentation.
|
||||
*/
|
||||
protected static function finishedEnumerating (clazz :Class) :void
|
||||
{
|
||||
_blocked[clazz] = true;
|
||||
}
|
||||
|
||||
/** The String name of this enum value. */
|
||||
protected var _name :String;
|
||||
|
||||
/** An array of enums for each enum class. */
|
||||
private static const _enums :Dictionary = new Dictionary(true);
|
||||
|
||||
/** Is further instantiation of enum constants for a class allowed? */
|
||||
private static const _blocked :Dictionary = new Dictionary(true);
|
||||
|
||||
finishedEnumerating(Enum); // do not allow any enums in this base class
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* An interface we can use to implement equals(), which is standard and
|
||||
* very useful in Java.
|
||||
*/
|
||||
public interface Equalable
|
||||
{
|
||||
/**
|
||||
* Returns true to indicate that the specified object is equal to
|
||||
* this instance.
|
||||
*/
|
||||
function equals (other :Object) :Boolean;
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.EventDispatcher;
|
||||
import flash.events.TimerEvent;
|
||||
|
||||
import flash.utils.getTimer; // function import
|
||||
import flash.utils.Timer;
|
||||
|
||||
/**
|
||||
* Dispatched when a set element expires.
|
||||
*
|
||||
* @eventType com.threerings.util.ExpiringSet.ELEMENT_EXPIRED
|
||||
*/
|
||||
[Event(name="ElementExpired", type="com.threerings.util.ValueEvent")]
|
||||
|
||||
/**
|
||||
* Data structure that keeps its elements for a short time, and then removes them automatically.
|
||||
* Note that expirations are done based on Timer granularity, so you may pull a value out
|
||||
* that technicaly should be expired, but only by a few milliseconds.
|
||||
*
|
||||
* All operations are O(n), including add().
|
||||
*/
|
||||
public class ExpiringSet extends EventDispatcher
|
||||
implements Set
|
||||
{
|
||||
/** The even that is dispatched when a member of this set expires. */
|
||||
public static const ELEMENT_EXPIRED :String = "ElementExpired";
|
||||
|
||||
/**
|
||||
* Initializes the expiring set.
|
||||
*
|
||||
* @param ttl Time to live value for set elements, in milliseconds.
|
||||
* @param expireHandler a function to be conveniently registered as an event listener.
|
||||
*/
|
||||
public function ExpiringSet (ttl :int, expireHandler :Function = null)
|
||||
{
|
||||
_ttl = ttl;
|
||||
_timer = new Timer(_ttl, 1);
|
||||
_timer.addEventListener(TimerEvent.TIMER, checkTimer);
|
||||
|
||||
if (expireHandler != null) {
|
||||
addEventListener(ELEMENT_EXPIRED, expireHandler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time to live value for this ExpiringSet. This value cannot be changed after
|
||||
* set creation.
|
||||
*/
|
||||
public function get ttl () :int
|
||||
{
|
||||
return _ttl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calling this function will not expire the elements, it simply removes them. No
|
||||
* ValueEvent will be dispatched.
|
||||
*/
|
||||
public function clear () :void
|
||||
{
|
||||
// simply trunate the data array
|
||||
_data.length = 0;
|
||||
_timer.stop();
|
||||
}
|
||||
|
||||
// from Set
|
||||
public function forEach (fn :Function) :void
|
||||
{
|
||||
for each (var e :ExpiringElement in _data) {
|
||||
fn(e.element);
|
||||
}
|
||||
}
|
||||
|
||||
// from Set
|
||||
public function size () :int
|
||||
{
|
||||
return _data.length;
|
||||
}
|
||||
|
||||
// from Set
|
||||
public function isEmpty () :Boolean
|
||||
{
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
// from Set
|
||||
public function contains (o :Object) :Boolean
|
||||
{
|
||||
return _data.some(function (e :ExpiringElement, ... ignored) :Boolean {
|
||||
return e.objectEquals(o);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that if you add an object that the list already contains, this method will return
|
||||
* false, but it will also update the expire time on that object to be this sets ttl from now,
|
||||
* as if the item really were being added to the list now.
|
||||
*/
|
||||
public function add (o :Object) :Boolean
|
||||
{
|
||||
var added :Boolean = true;
|
||||
for (var ii :int = 0; ii < _data.length; ii++) {
|
||||
if (ExpiringElement(_data[ii]).objectEquals(o)) {
|
||||
// already contained, remove this one and re-add at end
|
||||
_data.splice(ii, 1);
|
||||
added = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// push the item onto the queue. since each element has the same TTL, elements end up
|
||||
// being ordered by their expiration time.
|
||||
_data.push(new ExpiringElement(o, getTimer() + _ttl));
|
||||
if (_data.length == 1) {
|
||||
// set up the timer to remove this one element, otherwise it was already running
|
||||
_timer.reset();
|
||||
_timer.delay = _ttl;
|
||||
_timer.start();
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
// from Set
|
||||
public function remove (o :Object) :Boolean
|
||||
{
|
||||
// pull the item from anywhere in the queue. If we remove the first element, the timer
|
||||
// will harmlessly NOOP when it wakes up
|
||||
for (var ii :int = 0; ii < _data.length; ii++) {
|
||||
if (ExpiringElement(_data[ii]).objectEquals(o)) {
|
||||
_data.splice(ii, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation of Set returns a fresh array that it will never reference again.
|
||||
* Modification of this array will not change the ExpiringSet's structure.
|
||||
*/
|
||||
public function toArray () :Array
|
||||
{
|
||||
return _data.map(function (e :ExpiringElement, ... ignored) :Object {
|
||||
return e.element;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove probably just one element from the front of the expiration queue, and
|
||||
* schedule a wakeup for the time to the new head's expiration time.
|
||||
*/
|
||||
protected function checkTimer (... ignored) :void
|
||||
{
|
||||
var now :int = getTimer();
|
||||
while (_data.length > 0) {
|
||||
var e :ExpiringElement = ExpiringElement(_data[0]);
|
||||
var timeToExpire :int = e.expirationTime - now;
|
||||
if (timeToExpire <= 0) {
|
||||
_data.shift(); // remove it
|
||||
dispatchEvent(new ValueEvent(ELEMENT_EXPIRED, e.element)); // notify
|
||||
|
||||
} else {
|
||||
// the head element is not yet expired
|
||||
_timer.reset();
|
||||
_timer.delay = timeToExpire;
|
||||
_timer.start();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static const log :Log = Log.getLog(ExpiringSet);
|
||||
|
||||
/** The time to live for this set, not to be changed after construction. */
|
||||
protected /* final */ var _ttl :int;
|
||||
|
||||
/** Array of ExpiringElement instances, sorted by expiration time. */
|
||||
protected var _data :Array = [];
|
||||
|
||||
protected var _timer :Timer;
|
||||
}
|
||||
}
|
||||
|
||||
import com.threerings.util.Util;
|
||||
|
||||
class ExpiringElement
|
||||
{
|
||||
public var expirationTime :int;
|
||||
public var element :Object;
|
||||
|
||||
public function ExpiringElement (element :Object, expiration :int)
|
||||
{
|
||||
this.element = element;
|
||||
this.expirationTime = expiration;
|
||||
}
|
||||
|
||||
public function objectEquals (element :Object) :Boolean
|
||||
{
|
||||
return Util.equals(element, this.element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public class FileUtil
|
||||
{
|
||||
/**
|
||||
* Returns the substring composed of the characters after the last '.' in the supplied string.
|
||||
* The substring will be converted to lowercase.
|
||||
*/
|
||||
public static function getDotSuffix (filename :String) :String
|
||||
{
|
||||
var dotIndex :int = filename.lastIndexOf(".");
|
||||
return (dotIndex >= 0 ? filename.substr(dotIndex + 1).toLowerCase() : "");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,352 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.errors.IllegalOperationError;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* An implementation of a HashMap in actionscript. Any object (and null) may
|
||||
* be used as a key. Simple keys (Number, int, uint, Boolean, String) utilize
|
||||
* a Dictionary internally for storage; keys that implement Hashable are
|
||||
* stored efficiently, and any other key can also be used if the equalsFn
|
||||
* and hashFn are specified to the constructor.
|
||||
*/
|
||||
public class HashMap
|
||||
implements Map
|
||||
{
|
||||
/**
|
||||
* Construct a HashMap
|
||||
*
|
||||
* @param loadFactor - A measure of how full the hashtable is allowed to
|
||||
* get before it is automatically resized. The default
|
||||
* value of 1.75 should be fine.
|
||||
* @param equalsFn - (Optional) A function to use to compare object
|
||||
* equality for keys that are neither simple nor
|
||||
* implement Hashable. The signature should be
|
||||
* "function (o1, o2) :Boolean".
|
||||
* @param hashFn - (Optional) A function to use to generate a hash
|
||||
* code for keys that are neither simple nor
|
||||
* implement Hashable. The signature should be
|
||||
* "function (obj) :*", where the return type is
|
||||
* numeric or String. Two objects that are equals
|
||||
* according to the specified equalsFn <b>must</b>
|
||||
* generate equal values when passed to the hashFn.
|
||||
*/
|
||||
public function HashMap (
|
||||
loadFactor :Number = 1.75,
|
||||
equalsFn :Function = null,
|
||||
hashFn :Function = null)
|
||||
{
|
||||
if ((equalsFn != null) != (hashFn != null)) {
|
||||
throw new ArgumentError("Both the equals and hash functions " +
|
||||
"must be specified, or neither.");
|
||||
}
|
||||
_loadFactor = loadFactor;
|
||||
_equalsFn = equalsFn;
|
||||
_hashFn = hashFn;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function clear () :void
|
||||
{
|
||||
_simpleData = null;
|
||||
_simpleSize = 0;
|
||||
_entries = null;
|
||||
_entriesSize = 0;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function containsKey (key :Object) :Boolean
|
||||
{
|
||||
return (undefined !== get(key));
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function get (key :Object) :*
|
||||
{
|
||||
if (isSimple(key)) {
|
||||
return (_simpleData == null) ? undefined : _simpleData[key];
|
||||
}
|
||||
|
||||
if (_entries == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var hkey :Hashable = keyFor(key);
|
||||
var hash :int = hkey.hashCode();
|
||||
var index :int = indexFor(hash);
|
||||
var e :HashMap_Entry = (_entries[index] as HashMap_Entry);
|
||||
while (e != null) {
|
||||
if (e.hash == hash && e.key.equals(hkey)) {
|
||||
return e.value;
|
||||
}
|
||||
e = e.next;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function isEmpty () :Boolean
|
||||
{
|
||||
return (size() == 0);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function put (key :Object, value :Object) :*
|
||||
{
|
||||
var oldValue :*;
|
||||
if (isSimple(key)) {
|
||||
if (_simpleData == null) {
|
||||
_simpleData = new Dictionary();
|
||||
}
|
||||
|
||||
oldValue = _simpleData[key];
|
||||
_simpleData[key] = value;
|
||||
if (oldValue === undefined) {
|
||||
_simpleSize++;
|
||||
}
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
// lazy-create the array holding other hashables
|
||||
if (_entries == null) {
|
||||
_entries = [];
|
||||
_entries.length = DEFAULT_BUCKETS;
|
||||
}
|
||||
|
||||
var hkey :Hashable = keyFor(key);
|
||||
var hash :int = hkey.hashCode();
|
||||
var index :int = indexFor(hash);
|
||||
var firstEntry :HashMap_Entry = (_entries[index] as HashMap_Entry);
|
||||
for (var e :HashMap_Entry = firstEntry; e != null; e = e.next) {
|
||||
if (e.hash == hash && e.key.equals(hkey)) {
|
||||
oldValue = e.value;
|
||||
e.value = value;
|
||||
return oldValue; // size did not change
|
||||
}
|
||||
}
|
||||
|
||||
_entries[index] = new HashMap_Entry(hash, hkey, value, firstEntry);
|
||||
_entriesSize++;
|
||||
// check to see if we should grow the map
|
||||
if (_entriesSize > _entries.length * _loadFactor) {
|
||||
resize(2 * _entries.length);
|
||||
}
|
||||
// indicate that there was no value previously stored for the key
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function remove (key :Object) :*
|
||||
{
|
||||
if (isSimple(key)) {
|
||||
if (_simpleData == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var oldValue :* = _simpleData[key];
|
||||
if (oldValue !== undefined) {
|
||||
_simpleSize--;
|
||||
}
|
||||
delete _simpleData[key];
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
if (_entries == null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var hkey :Hashable = keyFor(key);
|
||||
var hash :int = hkey.hashCode();
|
||||
var index :int = indexFor(hash);
|
||||
var prev :HashMap_Entry = (_entries[index] as HashMap_Entry);
|
||||
var e :HashMap_Entry = prev;
|
||||
|
||||
while (e != null) {
|
||||
var next :HashMap_Entry = e.next;
|
||||
if (e.hash == hash && e.key.equals(hkey)) {
|
||||
if (prev == e) {
|
||||
_entries[index] = next;
|
||||
} else {
|
||||
prev.next = next;
|
||||
}
|
||||
_entriesSize--;
|
||||
// check to see if we should shrink the map
|
||||
if ((_entries.length > DEFAULT_BUCKETS) &&
|
||||
(_entriesSize < _entries.length * _loadFactor * .125)) {
|
||||
resize(Math.max(DEFAULT_BUCKETS, _entries.length / 2));
|
||||
}
|
||||
return e.value;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
|
||||
return undefined; // never found
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function size () :int
|
||||
{
|
||||
return _simpleSize + _entriesSize;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function keys () :Array
|
||||
{
|
||||
var keys :Array = [];
|
||||
forEach0(function (k :*, v :*) :void {
|
||||
keys.push(k);
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function values () :Array
|
||||
{
|
||||
var vals :Array = [];
|
||||
forEach0(function (k :*, v :*) :void {
|
||||
vals.push(v);
|
||||
});
|
||||
return vals;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function forEach (fn :Function) :void
|
||||
{
|
||||
forEach0(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal forEach.
|
||||
* @private
|
||||
*/
|
||||
protected function forEach0 (fn :Function) :void
|
||||
{
|
||||
if (_simpleData != null) {
|
||||
for (var key :Object in _simpleData) {
|
||||
fn(key, _simpleData[key]);
|
||||
}
|
||||
}
|
||||
|
||||
if (_entries != null) {
|
||||
for (var ii :int = _entries.length - 1; ii >= 0; ii--) {
|
||||
for (var e :HashMap_Entry = (_entries[ii] as HashMap_Entry); e != null;
|
||||
e = e.next) {
|
||||
fn(e.getOriginalKey(), e.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Hashable that represents the key.
|
||||
* @private
|
||||
*/
|
||||
protected function keyFor (key :Object) :Hashable
|
||||
{
|
||||
if (key is Hashable) {
|
||||
return (key as Hashable);
|
||||
|
||||
} else if (_hashFn == null) {
|
||||
throw new IllegalOperationError("Illegal key specified " +
|
||||
"for HashMap created without hashing functions.");
|
||||
|
||||
} else {
|
||||
return new HashMap_KeyWrapper(key, _equalsFn, _hashFn);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an index for the specified hashcode.
|
||||
* @private
|
||||
*/
|
||||
protected function indexFor (hash :int) :int
|
||||
{
|
||||
// TODO: improve?
|
||||
return Math.abs(hash) % _entries.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the specified key may be used to store values in a
|
||||
* Dictionary object.
|
||||
* @private
|
||||
*/
|
||||
protected function isSimple (key :Object) :Boolean
|
||||
{
|
||||
return (key == null) || (key is String) || (key is Number) || (key is Boolean) ||
|
||||
(key is Enum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resize the entries with Hashable keys to optimize
|
||||
* the memory/performance tradeoff.
|
||||
* @private
|
||||
*/
|
||||
protected function resize (newSize :int) :void
|
||||
{
|
||||
var oldEntries :Array = _entries;
|
||||
_entries = [];
|
||||
_entries.length = newSize;
|
||||
|
||||
// place all the old entries in the new map
|
||||
for (var ii :int = 0; ii < oldEntries.length; ii++) {
|
||||
var e :HashMap_Entry = (oldEntries[ii] as HashMap_Entry);
|
||||
while (e != null) {
|
||||
var next :HashMap_Entry = e.next;
|
||||
var index :int = indexFor(e.hash);
|
||||
e.next = (_entries[index] as HashMap_Entry);
|
||||
_entries[index] = e;
|
||||
e = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The current number of key/value pairs stored in the Dictionary. @private */
|
||||
protected var _simpleSize :int = 0;
|
||||
|
||||
/** The current number of key/value pairs stored in the _entries. @private */
|
||||
protected var _entriesSize :int = 0;
|
||||
|
||||
/** The load factor. @private */
|
||||
protected var _loadFactor :Number;
|
||||
|
||||
/** If non-null, contains simple key/value pairs. @private */
|
||||
protected var _simpleData :Dictionary
|
||||
|
||||
/** If non-null, contains Hashable keys and their values. @private */
|
||||
protected var _entries :Array;
|
||||
|
||||
/** The hashing function to use for non-Hashable complex keys. @private */
|
||||
protected var _hashFn :Function;
|
||||
|
||||
/** The equality function to use for non-Hashable complex keys. @private */
|
||||
protected var _equalsFn :Function;
|
||||
|
||||
/** The default size for the bucketed hashmap. @private */
|
||||
protected static const DEFAULT_BUCKETS :int = 16;
|
||||
}
|
||||
|
||||
} // end: package com.threerings.util
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import com.threerings.util.Hashable;
|
||||
|
||||
/**
|
||||
* A key/value pair in a HashMap. This is really an internal class to HashMap, and when
|
||||
* Flash CS4 is fixed, it will go nestle back into HashMap.as's luxurious folds.
|
||||
*/
|
||||
public class HashMap_Entry
|
||||
{
|
||||
public var key :Hashable;
|
||||
public var value :Object;
|
||||
public var hash :int;
|
||||
public var next :HashMap_Entry;
|
||||
|
||||
public function HashMap_Entry (hash :int, key :Hashable, value :Object, next :HashMap_Entry)
|
||||
{
|
||||
this.hash = hash;
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the original key used to store this entry.
|
||||
*/
|
||||
public function getOriginalKey () :Object
|
||||
{
|
||||
if (key is HashMap_KeyWrapper) {
|
||||
return (key as HashMap_KeyWrapper).key;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import com.threerings.util.Hashable;
|
||||
|
||||
/**
|
||||
* A wrapped key object HashMap. This is really an internal class to HashMap, and when
|
||||
* Flash CS4 is fixed, it will go nestle back into HashMap.as's luxurious folds.
|
||||
*/
|
||||
public class HashMap_KeyWrapper
|
||||
implements Hashable
|
||||
{
|
||||
public var key :Object;
|
||||
|
||||
public function HashMap_KeyWrapper (
|
||||
key :Object, equalsFn :Function, hashFn :Function)
|
||||
{
|
||||
this.key = key;
|
||||
_equalsFn = equalsFn;
|
||||
var hashValue :* = hashFn(key);
|
||||
if (hashValue is String) {
|
||||
var uid :String = (hashValue as String);
|
||||
// examine at most 32 characters of the string
|
||||
var inc :int = int(Math.max(1, Math.ceil(uid.length / 32)));
|
||||
for (var ii :int = 0; ii < uid.length; ii += inc) {
|
||||
_hash = (_hash << 1) ^ int(uid.charCodeAt(ii));
|
||||
}
|
||||
|
||||
} else {
|
||||
_hash = int(hashValue);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface Hashable
|
||||
public function equals (other :Object) :Boolean
|
||||
{
|
||||
return (other is HashMap_KeyWrapper) &&
|
||||
Boolean(_equalsFn(key, (other as HashMap_KeyWrapper).key));
|
||||
}
|
||||
|
||||
// documentation inherited from interface Hashable
|
||||
public function hashCode () :int
|
||||
{
|
||||
return _hash;
|
||||
}
|
||||
|
||||
protected var _key :Object;
|
||||
protected var _hash :int;
|
||||
protected var _equalsFn :Function;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.utils.ObjectUtil;
|
||||
|
||||
/**
|
||||
* A HashMap implementation that utilizes ObjectUtil.compare and
|
||||
* ObjectUtil.toString for hashing keys that are non-simple and do
|
||||
* not implement Hashable.
|
||||
*/
|
||||
public class HashObjectMap extends HashMap
|
||||
{
|
||||
public function HashObjectMap (loadFactor :Number = 1.75)
|
||||
{
|
||||
super(loadFactor,
|
||||
function (o1 :Object, o2 :Object) :Boolean {
|
||||
return (0 == ObjectUtil.compare(o1, o2));
|
||||
},
|
||||
ObjectUtil.toString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public class HashSet
|
||||
implements Set
|
||||
{
|
||||
/**
|
||||
* Construct a HashSet
|
||||
*
|
||||
* @param loadFactor - A measure of how full the hashtable is allowed to
|
||||
* get before it is automatically resized. The default
|
||||
* value of 1.75 should be fine.
|
||||
* @param equalsFn - (Optional) A function to use to compare object
|
||||
* equality for keys that are neither simple nor
|
||||
* implement Hashable. The signature should be
|
||||
* "function (o1, o2) :Boolean".
|
||||
* @param hashFn - (Optional) A function to use to generate a hash
|
||||
* code for keys that are neither simple nor
|
||||
* implement Hashable. The signature should be
|
||||
* "function (obj) :*", where the return type is
|
||||
* numeric or String. Two objects that are equals
|
||||
* according to the specified equalsFn *must*
|
||||
* generate equal values when passed to the hashFn.
|
||||
*/
|
||||
public function HashSet (
|
||||
loadFactor :Number = 1.75,
|
||||
equalsFn :Function = null,
|
||||
hashFn :Function = null)
|
||||
{
|
||||
_hashMap = new HashMap(loadFactor, equalsFn, hashFn);
|
||||
}
|
||||
|
||||
public function add (o :Object) :Boolean
|
||||
{
|
||||
var previousValue :* = _hashMap.put(o, null);
|
||||
|
||||
// return true if the key did not already exist in the Set
|
||||
return (undefined === previousValue);
|
||||
}
|
||||
|
||||
public function remove (o :Object) :Boolean
|
||||
{
|
||||
var previousValue :* = _hashMap.remove(o);
|
||||
|
||||
// return true if the key existed in the Set
|
||||
return (undefined !== previousValue);
|
||||
}
|
||||
|
||||
public function clear () :void
|
||||
{
|
||||
_hashMap.clear();
|
||||
}
|
||||
|
||||
public function contains (o :Object) :Boolean
|
||||
{
|
||||
return (_hashMap.containsKey(o));
|
||||
}
|
||||
|
||||
public function forEach (fn :Function) :void
|
||||
{
|
||||
_hashMap.forEach(function (key :Object, val :Object) :void {
|
||||
fn(key);
|
||||
});
|
||||
}
|
||||
|
||||
public function size () :int
|
||||
{
|
||||
return (_hashMap.size());
|
||||
}
|
||||
|
||||
public function isEmpty () :Boolean
|
||||
{
|
||||
return (_hashMap.isEmpty());
|
||||
}
|
||||
|
||||
public function toArray () :Array
|
||||
{
|
||||
return (_hashMap.keys());
|
||||
}
|
||||
|
||||
protected var _hashMap :HashMap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public interface Hashable extends Equalable
|
||||
{
|
||||
/**
|
||||
* Get a hashcode for this Equalable object so that it may be placed
|
||||
* in a HashMap.
|
||||
*/
|
||||
function hashCode () :int;
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.Proxy;
|
||||
import flash.utils.flash_proxy;
|
||||
|
||||
use namespace flash_proxy;
|
||||
|
||||
/**
|
||||
* Acts like the passed-in Object, but prevents modifications.
|
||||
*/
|
||||
public class ImmutableProxyObject extends Proxy
|
||||
{
|
||||
public function ImmutableProxyObject (source :Object, throwErrors :Boolean = true)
|
||||
{
|
||||
_source = source;
|
||||
_throwErrors = throwErrors;
|
||||
}
|
||||
|
||||
// public function hasOwnProperty (name :String) :Boolean
|
||||
// {
|
||||
// return _source.hasOwnProperty(name);
|
||||
// }
|
||||
//
|
||||
// public function isPrototypeOf (theClass :Object) :Boolean
|
||||
// {
|
||||
// return _source.isPrototypeOf(theClass);
|
||||
// }
|
||||
//
|
||||
// public function propertyIsEnumerable (name :String) :Boolean
|
||||
// {
|
||||
// return _source.propertyIsEnumerable(name);
|
||||
// }
|
||||
//
|
||||
// public function setPropertyIsEnumerable (name :String, isEnum :Boolean = true) :void
|
||||
// {
|
||||
// immutable();
|
||||
// }
|
||||
|
||||
public function toString () :String
|
||||
{
|
||||
return _source.toString();
|
||||
}
|
||||
|
||||
// valueOf ?
|
||||
|
||||
override flash_proxy function callProperty (name :*, ... rest) :*
|
||||
{
|
||||
Function(_source[name]).apply(null, rest);
|
||||
}
|
||||
|
||||
override flash_proxy function deleteProperty (name :*) :Boolean
|
||||
{
|
||||
immutable();
|
||||
return false;
|
||||
}
|
||||
|
||||
// omitted: getDescendants
|
||||
|
||||
override flash_proxy function getProperty (key :*) :*
|
||||
{
|
||||
return _source[key];
|
||||
}
|
||||
|
||||
override flash_proxy function hasProperty (key :*) :Boolean
|
||||
{
|
||||
return (key in _source);
|
||||
}
|
||||
|
||||
// omitted: isAttribute
|
||||
|
||||
override flash_proxy function nextName (index :int) :String
|
||||
{
|
||||
return _itrKeys[index - 1];
|
||||
}
|
||||
|
||||
override flash_proxy function nextNameIndex (index :int) :int
|
||||
{
|
||||
if (index == 0) {
|
||||
_itrKeys = Util.keys(_source);
|
||||
}
|
||||
|
||||
if (index < _itrKeys.length) {
|
||||
return index + 1;
|
||||
} else {
|
||||
_itrKeys = null;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
override flash_proxy function nextValue (index :int) :*
|
||||
{
|
||||
return _source[_itrKeys[index - 1]];
|
||||
}
|
||||
|
||||
override flash_proxy function setProperty (name :*, value :*) :void
|
||||
{
|
||||
immutable();
|
||||
}
|
||||
|
||||
protected function immutable () :void
|
||||
{
|
||||
if (_throwErrors) {
|
||||
throw new Error("You may not modify this object.");
|
||||
}
|
||||
}
|
||||
|
||||
protected var _source :Object;
|
||||
|
||||
protected var _throwErrors :Boolean;
|
||||
|
||||
protected var _itrKeys :Array;
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Java has Iterator, ActionScript has IViewCursor.
|
||||
* The problem is, IViewCursor defines 14 methods and 5 read-only properties.
|
||||
* That is a serious PITA to write for every collection that might desire
|
||||
* iteration. This provides a simpler alternative.
|
||||
*/
|
||||
public interface Iterator
|
||||
{
|
||||
/**
|
||||
* Is there another element available?
|
||||
*/
|
||||
function hasNext () :Boolean;
|
||||
|
||||
/**
|
||||
* Returns the next element.
|
||||
*/
|
||||
function next () :Object;
|
||||
|
||||
/**
|
||||
* Remove the last returned element.
|
||||
*/
|
||||
function remove () :void;
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Keycode constants.
|
||||
*
|
||||
* (The Flex documentation incorrectly indicates that flash.ui.Keyboard has all these
|
||||
* constants defined.)
|
||||
*/
|
||||
public class KeyboardCodes
|
||||
{
|
||||
public static const A :uint = 65;
|
||||
public static const B :uint = 66;
|
||||
public static const C :uint = 67;
|
||||
public static const D :uint = 68;
|
||||
public static const E :uint = 69;
|
||||
public static const F :uint = 70;
|
||||
public static const G :uint = 71;
|
||||
public static const H :uint = 72;
|
||||
public static const I :uint = 73;
|
||||
public static const J :uint = 74;
|
||||
public static const K :uint = 75;
|
||||
public static const L :uint = 76;
|
||||
public static const M :uint = 77;
|
||||
public static const N :uint = 78;
|
||||
public static const O :uint = 79;
|
||||
public static const P :uint = 80;
|
||||
public static const Q :uint = 81;
|
||||
public static const R :uint = 82;
|
||||
public static const S :uint = 83;
|
||||
public static const T :uint = 84;
|
||||
public static const U :uint = 85;
|
||||
public static const V :uint = 86;
|
||||
public static const W :uint = 87;
|
||||
public static const X :uint = 88;
|
||||
public static const Y :uint = 89;
|
||||
public static const Z :uint = 90;
|
||||
|
||||
public static const NUMBER_0 :uint = 48;
|
||||
public static const NUMBER_1 :uint = 49;
|
||||
public static const NUMBER_2 :uint = 50;
|
||||
public static const NUMBER_3 :uint = 51;
|
||||
public static const NUMBER_4 :uint = 52;
|
||||
public static const NUMBER_5 :uint = 53;
|
||||
public static const NUMBER_6 :uint = 54;
|
||||
public static const NUMBER_7 :uint = 55;
|
||||
public static const NUMBER_8 :uint = 56;
|
||||
public static const NUMBER_9 :uint = 57;
|
||||
|
||||
public static const NUMPAD_0 :uint = 96;
|
||||
public static const NUMPAD_1 :uint = 97;
|
||||
public static const NUMPAD_2 :uint = 98;
|
||||
public static const NUMPAD_3 :uint = 99;
|
||||
public static const NUMPAD_4 :uint = 100;
|
||||
public static const NUMPAD_5 :uint = 101;
|
||||
public static const NUMPAD_6 :uint = 102;
|
||||
public static const NUMPAD_7 :uint = 103;
|
||||
public static const NUMPAD_8 :uint = 104;
|
||||
public static const NUMPAD_9 :uint = 105;
|
||||
|
||||
public static const NUMPAD_ADD :uint = 107;
|
||||
public static const NUMPAD_DECIMAL :uint = 110;
|
||||
public static const NUMPAD_DIVIDE :uint = 111;
|
||||
public static const NUMPAD_ENTER :uint = 108;
|
||||
public static const NUMPAD_MULTIPLY :uint = 106;
|
||||
public static const NUMPAD_SUBTRACT :uint = 109;
|
||||
|
||||
public static const F1 :uint = 112;
|
||||
public static const F2 :uint = 113;
|
||||
public static const F3 :uint = 114;
|
||||
public static const F4 :uint = 115;
|
||||
public static const F5 :uint = 116;
|
||||
public static const F6 :uint = 117;
|
||||
public static const F7 :uint = 118;
|
||||
public static const F8 :uint = 119;
|
||||
public static const F9 :uint = 120;
|
||||
public static const F10 :uint = 121;
|
||||
public static const F11 :uint = 122;
|
||||
public static const F12 :uint = 123;
|
||||
public static const F13 :uint = 124;
|
||||
public static const F14 :uint = 125;
|
||||
public static const F15 :uint = 126;
|
||||
|
||||
public static const LEFT :uint = 37;
|
||||
public static const UP :uint = 38;
|
||||
public static const RIGHT :uint = 39;
|
||||
public static const DOWN :uint = 40;
|
||||
|
||||
public static const ALTERNATE :uint = 18;
|
||||
public static const BACKQUOTE :uint = 192;
|
||||
public static const BACKSLASH :uint = 220;
|
||||
public static const BACKSPACE :uint = 8;
|
||||
public static const CAPS_LOCK :uint = 20;
|
||||
public static const COMMA :uint = 188;
|
||||
public static const COMMAND :uint = 15;
|
||||
public static const CONTROL :uint = 17;
|
||||
public static const DELETE :uint = 46;
|
||||
public static const END :uint = 35;
|
||||
public static const ENTER :uint = 13;
|
||||
public static const EQUAL :uint = 187;
|
||||
public static const ESCAPE :uint = 27;
|
||||
public static const HOME :uint = 36;
|
||||
public static const INSERT :uint = 45;
|
||||
public static const LEFTBRACKET :uint = 219;
|
||||
public static const MINUS :uint = 189;
|
||||
public static const PAGE_DOWN :uint = 34;
|
||||
public static const PAGE_UP :uint = 33;
|
||||
public static const PERIOD :uint = 190;
|
||||
public static const QUOTE :uint = 222;
|
||||
public static const RIGHTBRACKET :uint = 221;
|
||||
public static const SEMICOLON :uint = 186;
|
||||
public static const SHIFT :uint = 16;
|
||||
public static const SLASH :uint = 191;
|
||||
public static const SPACE :uint = 32;
|
||||
public static const TAB :uint = 9;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.geom.Point;
|
||||
import flash.geom.Matrix;
|
||||
|
||||
/**
|
||||
* Merely a typed container for two Points.
|
||||
*/
|
||||
public class LineSegment implements Equalable
|
||||
{
|
||||
public static const INTERSECTION_NORTH :int = 1;
|
||||
public static const INTERSECTION_SOUTH :int = 2;
|
||||
public static const DOES_NOT_INTERSECT :int = 3;
|
||||
|
||||
public var start :Point;
|
||||
public var stop :Point;
|
||||
|
||||
public function LineSegment (start :Point, stop :Point)
|
||||
{
|
||||
this.start = start;
|
||||
this.stop = stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the length of this line.
|
||||
*/
|
||||
public function getLength () :Number
|
||||
{
|
||||
return Point.distance(start, stop);
|
||||
}
|
||||
|
||||
public function isIntersected (line :LineSegment) :Boolean
|
||||
{
|
||||
return getIntersectionType(line) != DOES_NOT_INTERSECT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the point at which the other line intersects us.
|
||||
*/
|
||||
public function getIntersectionPoint (line :LineSegment) :Point
|
||||
{
|
||||
return getIntersection(line, true) as Point;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the given line intersects this line. This method rotates both lines so that the
|
||||
* start point of this line is on the left, at (0, 0). If the lines do intersect, it then
|
||||
* returns INTERSECTION_NORTH if the end point of <code>line</code> is north of this line,
|
||||
* INTERSECTION_SOUTH otherwise.
|
||||
*
|
||||
* Intersections are inclusive. If one or both points lands on this line, interects will not
|
||||
* return DOES_NOT_INTERSECT.
|
||||
*/
|
||||
public function getIntersectionType (line :LineSegment) :int
|
||||
{
|
||||
return getIntersection(line, false) as int;
|
||||
}
|
||||
|
||||
// from interface Equalable
|
||||
public function equals (o :Object) :Boolean
|
||||
{
|
||||
var other :LineSegment = o as LineSegment; // or null if not a line
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// both end points must be the same, in the same order
|
||||
return start.equals(other.start) && stop.equals(other.stop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method that calculates whether the other line intersects
|
||||
* and returns either the intersected point or merely the intersection
|
||||
* type.
|
||||
*/
|
||||
protected function getIntersection (line :LineSegment, returnPoint :Boolean) :*
|
||||
{
|
||||
// rotate so that this line is horizontal, with the start on the left, at (0, 0)
|
||||
var trans :Matrix = new Matrix();
|
||||
trans.translate(-start.x, -start.y);
|
||||
trans.rotate(-Math.atan2(stop.y - start.y, stop.x - start.x));
|
||||
var thisLineStop :Point = trans.transformPoint(stop);
|
||||
var thatLineStart :Point = trans.transformPoint(line.start);
|
||||
var thatLineStop :Point = trans.transformPoint(line.stop);
|
||||
var interp :Point;
|
||||
var type :int;
|
||||
|
||||
if (thatLineStart.y >= 0 && thatLineStop.y <= 0) {
|
||||
interp = Point.interpolate(thatLineStart, thatLineStop, thatLineStop.y /
|
||||
(thatLineStop.y + (-thatLineStart.y)));
|
||||
type = INTERSECTION_NORTH;
|
||||
|
||||
} else if (thatLineStart.y <= 0 && thatLineStop.y >= 0) {
|
||||
interp = Point.interpolate(thatLineStop, thatLineStart, thatLineStart.y /
|
||||
(thatLineStart.y + (-thatLineStop.y)));
|
||||
type = INTERSECTION_SOUTH;
|
||||
}
|
||||
|
||||
// see if we have a potential hit...
|
||||
if (interp != null && interp.x >= 0 && interp.x <= thisLineStop.x) {
|
||||
if (returnPoint) {
|
||||
// transform the intersection point back into "real" coordinates
|
||||
trans.invert();
|
||||
return trans.transformPoint(interp);
|
||||
|
||||
} else {
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
return returnPoint ? null : DOES_NOT_INTERSECT;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,345 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
/**
|
||||
* A simple logging mechanism.
|
||||
*
|
||||
* Log instances are created for modules, and the logging level can be configured per
|
||||
* module in a hierarchical fashion.
|
||||
*
|
||||
* Typically, you should create a module name based on the full path to a class:
|
||||
* calling getLog() and passing an object or Class will do this. Alternattely, you
|
||||
* may create a Log to share in several classes in a package, in which case the
|
||||
* module name can be like "com.foocorp.games.bunnywar". Finally, you can just
|
||||
* create made-up module names like "mygame" or "util", but this is not recommended.
|
||||
* You really should name things based on your packages, and your packages should be
|
||||
* named according to Sun's recommendations for Java packages.
|
||||
*
|
||||
* Typical usage for creating a Log to be used by the entire class would be:
|
||||
* public class MyClass
|
||||
* {
|
||||
* private static const log :Log = Log.getLog(MyClass);
|
||||
* ...
|
||||
*
|
||||
* OR, if you just need a one-off Log:
|
||||
* protected function doStuff (thingy :Thingy) :void
|
||||
* {
|
||||
* if (!isValid(thingy)) {
|
||||
* Log.getLog(this).warn("Invalid thingy specified", "thingy", thingy);
|
||||
* ....
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** Log level constants. */
|
||||
public static const DEBUG :int = 0;
|
||||
public static const INFO :int = 1;
|
||||
public static const WARNING :int = 2;
|
||||
public static const ERROR :int = 3;
|
||||
public static const OFF :int = 4;
|
||||
// if you add to this, update LEVEL_NAMES and stringToLevel() at the bottom...
|
||||
|
||||
/**
|
||||
* Retrieve a Log for the specified module.
|
||||
*
|
||||
* @param moduleSpec can be a String of the module name, or any Object or Class to
|
||||
* have the module name be the full package and name of the class (recommended).
|
||||
*/
|
||||
public static function getLog (moduleSpec :*) :Log
|
||||
{
|
||||
const module :String = (moduleSpec is String) ? String(moduleSpec)
|
||||
: getQualifiedClassName(moduleSpec).replace("::", ".");
|
||||
return new Log(module);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience function for quickly and easily inserting printy
|
||||
* statements during application development.
|
||||
*/
|
||||
public static function testing (... params) :void
|
||||
{
|
||||
var log :Log = new Log("testing");
|
||||
log.debug.apply(log, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience function for quickly printing a stack trace
|
||||
* to the log, useful for debugging.
|
||||
*/
|
||||
public static function dumpStack (msg :String = "dumpStack") :void
|
||||
{
|
||||
testing(new Error(msg).getStackTrace());
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a logging target.
|
||||
*/
|
||||
public static function addTarget (target :LogTarget) :void
|
||||
{
|
||||
_targets.push(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a logging target.
|
||||
*/
|
||||
public static function removeTarget (target :LogTarget) :void
|
||||
{
|
||||
var dex :int = _targets.indexOf(target);
|
||||
if (dex != -1) {
|
||||
_targets.splice(dex, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the log level for the specified module.
|
||||
*
|
||||
* @param module The smallest prefix desired to configure a log level.
|
||||
* For example, you can set the global level with Log.setLevel("", Log.INFO);
|
||||
* Then you can Log.setLevel("com.foo.game", Log.DEBUG). Now, everything
|
||||
* logs at INFO level except for modules within com.foo.game, which is at DEBUG.
|
||||
*/
|
||||
public static function setLevel (module :String, level :int) :void
|
||||
{
|
||||
_setLevels[module] = level;
|
||||
_levels = {}; // reset cached levels
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String in the form of ":info;com.foo.game:debug;com.bar.util:warning"
|
||||
*
|
||||
* Semicolons separate modules, colons separate a module name from the log level.
|
||||
* An empty string specifies the top-level (global) module.
|
||||
*/
|
||||
public static function setLevels (settingString :String) :void
|
||||
{
|
||||
for each (var module :String in settingString.split(";")) {
|
||||
var setting :Array = module.split(":");
|
||||
_setLevels[setting[0]] = stringToLevel(String(setting[1]));
|
||||
}
|
||||
_levels = {}; // reset cached levels
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Log.getLog();
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
public function Log (module :String)
|
||||
{
|
||||
if (module == null) module = "";
|
||||
_module = module;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message with 'debug' priority.
|
||||
*
|
||||
* @param args The first argument is the actual message to log. After that, each pair
|
||||
* of parameters is printed in key/value form, the benefit being that if no log
|
||||
* message is generated then toString() will not be called on the values.
|
||||
* A final parameter may be an Error, in which case the stack trace is printed.
|
||||
*
|
||||
* @example
|
||||
* <listing version="3.0">
|
||||
* log.debug("Message", "key1", value1, "key2", value2, optionalError);
|
||||
* </listing>
|
||||
*/
|
||||
public function debug (... args) :void
|
||||
{
|
||||
doLog(DEBUG, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message with 'info' priority.
|
||||
*
|
||||
* @param args The first argument is the actual message to log. After that, each pair
|
||||
* of parameters is printed in key/value form, the benefit being that if no log
|
||||
* message is generated then toString() will not be called on the values.
|
||||
* A final parameter may be an Error, in which case the stack trace is printed.
|
||||
*
|
||||
* @example
|
||||
* <listing version="3.0">
|
||||
* log.info("Message", "key1", value1, "key2", value2, optionalError);
|
||||
* </listing>
|
||||
*/
|
||||
public function info (... args) :void
|
||||
{
|
||||
doLog(INFO, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message with 'warning' priority.
|
||||
*
|
||||
* @param args The first argument is the actual message to log. After that, each pair
|
||||
* of parameters is printed in key/value form, the benefit being that if no log
|
||||
* message is generated then toString() will not be called on the values.
|
||||
* A final parameter may be an Error, in which case the stack trace is printed.
|
||||
*
|
||||
* @example
|
||||
* <listing version="3.0">
|
||||
* log.warning("Message", "key1", value1, "key2", value2, optionalError);
|
||||
* </listing>
|
||||
*/
|
||||
public function warning (... args) :void
|
||||
{
|
||||
doLog(WARNING, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a message with 'error' priority.
|
||||
*
|
||||
* @param args The first argument is the actual message to log. After that, each pair
|
||||
* of parameters is printed in key/value form, the benefit being that if no log
|
||||
* message is generated then toString() will not be called on the values.
|
||||
* A final parameter may be an Error, in which case the stack trace is printed.
|
||||
*
|
||||
* @example
|
||||
* <listing version="3.0">
|
||||
* log.error("Message", "key1", value1, "key2", value2, optionalError);
|
||||
* </listing>
|
||||
*/
|
||||
public function error (... args) :void
|
||||
{
|
||||
doLog(ERROR, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log just a stack trace with 'warning' priority.
|
||||
* Deprecated, sorta. Just use warning("Message", error);
|
||||
*/
|
||||
public function logStackTrace (error :Error) :void
|
||||
{
|
||||
warning(error.getStackTrace());
|
||||
}
|
||||
|
||||
protected function doLog (level :int, args :Array) :void
|
||||
{
|
||||
if (level < getLevel(_module)) {
|
||||
return; // we don't want to log it!
|
||||
}
|
||||
var logMessage :String = formatMessage(level, args);
|
||||
trace(logMessage);
|
||||
// possibly also dispatch to any other log targets.
|
||||
for each (var target :LogTarget in _targets) {
|
||||
target.log(logMessage);
|
||||
}
|
||||
}
|
||||
|
||||
protected function formatMessage (level :int, args :Array) :String
|
||||
{
|
||||
var msg :String = getTimeStamp() + " " + LEVEL_NAMES[level] + ": " + _module;
|
||||
if (args.length > 0) {
|
||||
msg += " " + String(args[0]); // the primary log message
|
||||
var err :Error = null;
|
||||
if (args.length % 2 == 0) { // there's one extra arg
|
||||
var lastArg :Object = args.pop();
|
||||
if (lastArg is Error) {
|
||||
err = lastArg as Error; // ok, it's an error, we like those
|
||||
} else if (lastArg == null) { // assume it's an error that's just null
|
||||
args.push("error", lastArg); // print "error=null"
|
||||
} else {
|
||||
args.push(lastArg, ""); // what? Well, cope by pushing it back with a ""
|
||||
}
|
||||
}
|
||||
if (args.length > 1) {
|
||||
for (var ii :int = 1; ii < args.length; ii += 2) {
|
||||
msg += (ii == 1) ? " [" : ", ";
|
||||
msg += String(args[ii]) + "=" + String(args[ii + 1]);
|
||||
}
|
||||
msg += "]";
|
||||
}
|
||||
if (err != null) {
|
||||
msg += "\n" + err.getStackTrace();
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
|
||||
protected function getTimeStamp () :String
|
||||
{
|
||||
var d :Date = new Date();
|
||||
// return d.toLocaleTimeString();
|
||||
|
||||
// format it like the date format in our java logs
|
||||
return d.fullYear + "-" +
|
||||
StringUtil.prepad(String(d.month + 1), 2, "0") + "-" +
|
||||
StringUtil.prepad(String(d.date), 2, "0") + " " +
|
||||
StringUtil.prepad(String(d.hours), 2, "0") + ":" +
|
||||
StringUtil.prepad(String(d.minutes), 2, "0") + ":" +
|
||||
StringUtil.prepad(String(d.seconds), 2, "0") + "," +
|
||||
StringUtil.prepad(String(d.milliseconds), 3, "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the logging level for the specified module.
|
||||
*/
|
||||
protected static function getLevel (module :String) :int
|
||||
{
|
||||
// we probably already have the level cached for this module
|
||||
var lev :Object = _levels[module];
|
||||
if (lev == null) {
|
||||
// cache miss- copy some parent module's level...
|
||||
var ancestor :String = module;
|
||||
while (true) {
|
||||
lev = _setLevels[ancestor];
|
||||
if (lev != null || ancestor == "") {
|
||||
// bail if we found a setting or get to the top level,
|
||||
// but always save the level from _setLevels into _levels
|
||||
_levels[module] = int(lev); // if lev was null, this will become 0 (DEBUG)
|
||||
break;
|
||||
}
|
||||
var dex :int = ancestor.lastIndexOf(".");
|
||||
ancestor = (dex == -1) ? "" : ancestor.substring(0, dex);
|
||||
}
|
||||
}
|
||||
return int(lev);
|
||||
}
|
||||
|
||||
protected static function stringToLevel (s :String) :int
|
||||
{
|
||||
switch (s.toLowerCase()) {
|
||||
default: // default to DEBUG
|
||||
case "debug": return DEBUG;
|
||||
case "info": return INFO;
|
||||
case "warning": case "warn": return WARNING;
|
||||
case "error": return ERROR;
|
||||
case "off": return OFF;
|
||||
}
|
||||
}
|
||||
|
||||
/** The module to which this log instance applies. */
|
||||
protected var _module :String;
|
||||
|
||||
/** Other registered LogTargets, besides the trace log. */
|
||||
protected static var _targets :Array = [];
|
||||
|
||||
/** A cache of log levels, copied from _setLevels. */
|
||||
protected static var _levels :Object = {};
|
||||
|
||||
/** The configured log levels. */
|
||||
protected static var _setLevels :Object = { "": DEBUG }; // global: debug
|
||||
|
||||
/** The outputted names of each level. The last one isn't used, it corresponds with OFF. */
|
||||
protected static const LEVEL_NAMES :Array = [ "debug", "INFO", "WARN", "ERROR", false ];
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* A very simple Logging interface used by Log.
|
||||
*/
|
||||
public interface LogTarget
|
||||
{
|
||||
/**
|
||||
* Log the specified message, which is already fully formatted.
|
||||
*/
|
||||
function log (msg :String) :void;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Utility methods relating to the electronic mails.
|
||||
*/
|
||||
public class MailUtil
|
||||
{
|
||||
/**
|
||||
* Returns true if the supplied email address appears valid (according to a widely used regular
|
||||
* expression). False if it does not.
|
||||
*/
|
||||
public static function isValidAddress (email :String) :Boolean
|
||||
{
|
||||
var matches :Array = email.match(EMAIL_REGEX);
|
||||
// in theory there should only be one match and we should check for 'matches.length == 1',
|
||||
// but Flash's regular expression code likes to return nonsensical things, so we just check
|
||||
// that the first match is equal to the entire supplied address
|
||||
return (matches != null && matches.length > 0) && ((matches[0] as String) == email);
|
||||
}
|
||||
|
||||
/** Originally formulated by lambert@nas.nasa.gov. */
|
||||
protected static const EMAIL_REGEX :RegExp = new RegExp(
|
||||
"^([-a-z0-9_.!%+]+@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[-a-z0-9]+)$", "i");
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* A Map is an object that maps keys to values.
|
||||
*/
|
||||
public interface Map
|
||||
{
|
||||
/**
|
||||
* Clear this map, removing all stored elements.
|
||||
*/
|
||||
function clear () :void;
|
||||
|
||||
/**
|
||||
* Returns true if the specified key exists in the map.
|
||||
*/
|
||||
function containsKey (key :Object) :Boolean;
|
||||
|
||||
/**
|
||||
* Retrieve the value stored in this map for the specified key.
|
||||
* Returns the value, or undefined if there is no mapping for the key.
|
||||
*/
|
||||
function get (key :Object) :*;
|
||||
|
||||
/**
|
||||
* Call the specified function, which accepts two args: key and value,
|
||||
* for every mapping.
|
||||
*/
|
||||
function forEach (fn :Function) :void;
|
||||
|
||||
/**
|
||||
* Returns true if this map contains no elements.
|
||||
*/
|
||||
function isEmpty () :Boolean;
|
||||
|
||||
/**
|
||||
* Return all the unique keys in this Map, in Array form.
|
||||
* The Array is not a 'view': it can be modified without disturbing
|
||||
* the Map from whence it came.
|
||||
*/
|
||||
function keys () :Array;
|
||||
|
||||
/**
|
||||
* Store a value in the map associated with the specified key.
|
||||
* Returns the previous value stored for that key, or undefined.
|
||||
*/
|
||||
function put (key :Object, value :Object) :*;
|
||||
|
||||
/**
|
||||
* Removes the mapping for the specified key.
|
||||
* Returns the value that had been stored, or undefined.
|
||||
*/
|
||||
function remove (key :Object) :*;
|
||||
|
||||
/**
|
||||
* Return the current size of the map.
|
||||
*/
|
||||
function size () :int;
|
||||
|
||||
/**
|
||||
* Return all the values in this Map, in Array form.
|
||||
* The Array is not a 'view': it can be modified without disturbing
|
||||
* the Map from whence it came.
|
||||
*/
|
||||
function values () :Array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.Proxy;
|
||||
import flash.utils.flash_proxy;
|
||||
|
||||
use namespace flash_proxy;
|
||||
|
||||
/**
|
||||
* Makes a Map behave like an object. Iterations are done in the order of the keys
|
||||
* returned by the Map's keys() function.
|
||||
* Warning: "for" loops convert all keys to Strings, due to limitations with Proxy.
|
||||
*/
|
||||
public class MapProxyObject extends Proxy
|
||||
{
|
||||
/**
|
||||
* Construct a MapProxyObject backed by the specified Map.
|
||||
*/
|
||||
public function MapProxyObject (source :Map)
|
||||
{
|
||||
_map = source;
|
||||
}
|
||||
|
||||
// The following are not strictly necessary and are freaking out asdoc, so I'll
|
||||
// comment them out for now.
|
||||
//
|
||||
// // from Object (but does not require 'override')
|
||||
// public function hasOwnProperty (name :String) :Boolean
|
||||
// {
|
||||
// return _map.containsKey(name);
|
||||
// }
|
||||
//
|
||||
// // from Object (but does not require 'override')
|
||||
// public function propertyIsEnumerable (name :String) :Boolean
|
||||
// {
|
||||
// return _map.containsKey(name);
|
||||
// }
|
||||
//
|
||||
// // from Object (but does not require 'override')
|
||||
// public function setPropertyIsEnumerable (name :String, isEnum :Boolean = true) :void
|
||||
// {
|
||||
// // no-op
|
||||
// // (Alternatively, we could keep a dictionary of keys that we ignore for enumeration...)
|
||||
// }
|
||||
|
||||
/**
|
||||
* Handle value = proxy[key].
|
||||
*/
|
||||
override flash_proxy function getProperty (key :*) :*
|
||||
{
|
||||
return _map.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle proxy[key] = value.
|
||||
*/
|
||||
override flash_proxy function setProperty (key :*, value :*) :void
|
||||
{
|
||||
_map.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle delete proxy[key].
|
||||
*/
|
||||
override flash_proxy function deleteProperty (key :*) :Boolean
|
||||
{
|
||||
return (_map.remove(key) !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when iterating with a "for" loop. Note well that keys are turned into
|
||||
* Strings. This is a major failing.
|
||||
*/
|
||||
override flash_proxy function nextName (index :int) :String
|
||||
{
|
||||
return StringUtil.toString(_itrKeys[index - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used when iterating with a "for each" loop.
|
||||
*/
|
||||
override flash_proxy function nextValue (index :int) :*
|
||||
{
|
||||
return _map.get(_itrKeys[index - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iteration support.
|
||||
*/
|
||||
override flash_proxy function nextNameIndex (index :int) :int
|
||||
{
|
||||
// on the first call, set up a list to be iterated
|
||||
if (index == 0) {
|
||||
_itrKeys = _map.keys();
|
||||
}
|
||||
|
||||
// return a 1-based index to indicate that there is a property
|
||||
if (index < _itrKeys.length) {
|
||||
return index + 1;
|
||||
|
||||
} else {
|
||||
_itrKeys = null; // we're done, clear the list
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** The map we use for storage. */
|
||||
protected var _map :Map;
|
||||
|
||||
/** A temporary ordering for iteration. */
|
||||
protected var _itrKeys :Array;
|
||||
}
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.resources.IResourceBundle;
|
||||
import mx.resources.ResourceManager;
|
||||
import mx.resources.IResourceManager;
|
||||
|
||||
/**
|
||||
* A message bundle provides an easy mechanism by which to obtain
|
||||
* translated message strings from a resource bundle. It uses the {@link
|
||||
* MessageFormat} class to substitute arguments into the translation
|
||||
* strings. Message bundles would generally be obtained via the {@link
|
||||
* MessageManager}, but could be constructed individually if so desired.
|
||||
*/
|
||||
public class MessageBundle
|
||||
{
|
||||
/**
|
||||
* Initializes the message bundle which will obtain localized messages
|
||||
* from the supplied resource bundle. The path is provided purely for
|
||||
* reporting purposes.
|
||||
*/
|
||||
public function init (msgmgr :MessageManager, path :String, parent :MessageBundle) :void
|
||||
{
|
||||
_msgmgr = msgmgr;
|
||||
_path = path;
|
||||
_parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path to this bundle.
|
||||
*/
|
||||
public function getPath () :String
|
||||
{
|
||||
return _path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we have a translation mapping for the supplied key,
|
||||
* false if not.
|
||||
*/
|
||||
public function exists (key :String) :Boolean
|
||||
{
|
||||
return getResourceString(key, false) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the messages that begin with the specified prefix.
|
||||
*
|
||||
* @param includeParent if true, messages from our parent bundle (and its
|
||||
* parent bundle, all the way up the chain will be included).
|
||||
*/
|
||||
public function getAll (prefix :String, includeParent :Boolean = true) :Array
|
||||
{
|
||||
var messages :Array = []
|
||||
for each (var value :Object in getAllMapped(prefix, includeParent)) {
|
||||
messages.push(value);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the messages and their keys that begin with the specified prefix.
|
||||
*/
|
||||
public function getAllMapped (prefix :String, includeParent :Boolean = true) :Object
|
||||
{
|
||||
var mgr :IResourceManager = ResourceManager.getInstance();
|
||||
|
||||
// search all locales, using the first one first, but including any
|
||||
// non-overridden keys from backing locales
|
||||
var messages :Object = {};
|
||||
var key :String;
|
||||
for each (var locale :String in mgr.getLocales()) {
|
||||
var bundle :IResourceBundle = mgr.getResourceBundle(locale, _path);
|
||||
if (bundle != null) {
|
||||
for (key in bundle.content) {
|
||||
// preserve the message found in an earlier locale
|
||||
if (StringUtil.startsWith(key, prefix) && !(key in messages)) {
|
||||
messages[key] = bundle.content[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeParent && _parent != null) {
|
||||
// don't let parent messages overwrite any we've found
|
||||
var parentMap :Object = _parent.getAllMapped(prefix, includeParent);
|
||||
for (key in parentMap) {
|
||||
if (!(key in messages)) {
|
||||
messages[key] = parentMap[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a String from the resource bundle, or null if there was an
|
||||
* error.
|
||||
*
|
||||
* @param key the resource key.
|
||||
* @param reportMissing whether or not the method should log an error
|
||||
* if the resource didn't exist.
|
||||
*/
|
||||
protected function getResourceString (key :String, reportMissing :Boolean = true) :String
|
||||
{
|
||||
var value :String = ResourceManager.getInstance().getString(_path, key);
|
||||
|
||||
if (value == null) {
|
||||
// if we have a parent, try getting the string from them
|
||||
if (_parent != null) {
|
||||
value = _parent.getResourceString(key, false);
|
||||
}
|
||||
// if we didn't find it in our parent, we want to fall
|
||||
// through and report missing appropriately
|
||||
if (value == null && reportMissing) {
|
||||
Log.getLog(this).warning("Missing translation message " +
|
||||
"[bundle=" + _path + ", key=" + key + "].");
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the translation for the specified message key. The
|
||||
* specified arguments are substituted into the translated string.
|
||||
*
|
||||
* <p> If the first argument in the array is an {@link Integer}
|
||||
* object, a translation will be selected accounting for plurality in
|
||||
* the following manner. Assume a message key of
|
||||
* <code>m.widgets</code>, the following translations should be
|
||||
* defined:
|
||||
* <pre>
|
||||
* m.widgets.0 = no widgets.
|
||||
* m.widgets.1 = {0} widget.
|
||||
* m.widgets.n = {0} widgets.
|
||||
* </pre>
|
||||
*
|
||||
* The specified argument is substituted into the translated string as
|
||||
* appropriate. Consider using:
|
||||
*
|
||||
* <pre>
|
||||
* m.widgets.n = {0,number,integer} widgets.
|
||||
* </pre>
|
||||
*
|
||||
* to obtain proper insertion of commas and dots as appropriate for
|
||||
* the locale.
|
||||
*
|
||||
* <p> See {@link MessageFormat} for more information on how the
|
||||
* substitution is performed. If a translation message does not exist
|
||||
* for the specified key, an error is logged and the key itself (plus
|
||||
* the arguments) is returned so that the caller need not worry about
|
||||
* handling a null response.
|
||||
*/
|
||||
public function get (key :String, ... args) :String
|
||||
{
|
||||
// if this string is tainted, we don't translate it, instead we
|
||||
// simply remove the taint character and return it to the caller
|
||||
if (key.charAt(0) === TAINT_CHAR) {
|
||||
return key.substring(1);
|
||||
}
|
||||
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
// if this is a qualified key, we need to pass the buck to the
|
||||
// appropriate message bundle
|
||||
if (key.indexOf(QUAL_PREFIX) == 0) {
|
||||
var qbundle :MessageBundle = _msgmgr.getBundle(getBundle(key));
|
||||
return qbundle.get(getUnqualifiedKey(key), args);
|
||||
}
|
||||
|
||||
// Select the proper suffix if our first argument can be coaxed into an integer
|
||||
var suffix :String = getSuffix(args);
|
||||
var msg :String = getResourceString(key + suffix, false);
|
||||
|
||||
if (msg == null) {
|
||||
if (suffix != "") {
|
||||
// Try the original key
|
||||
msg = getResourceString(key, false);
|
||||
}
|
||||
|
||||
// if the msg is still missing, we have a problem
|
||||
if (msg == null) {
|
||||
Log.getLog(this).warning("Missing translation message " +
|
||||
"[bundle=" + _path + ", key=" + key + "].");
|
||||
|
||||
// return something bogus
|
||||
return (key + args);
|
||||
}
|
||||
}
|
||||
|
||||
return StringUtil.substitute(msg, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper function for {@link #get(String,Object[])} that allows us
|
||||
* to automatically perform plurality processing if our first argument
|
||||
* can be coaxed to an {@link Integer}.
|
||||
*/
|
||||
protected function getSuffix (args :Array) :String
|
||||
{
|
||||
if (args.length > 0 && args[0] != null) {
|
||||
try {
|
||||
var count :int = (args[0] is int) ? int(args[0]) :
|
||||
StringUtil.parseInteger(String(args[0]));
|
||||
switch (count) {
|
||||
case 0: return ".0";
|
||||
case 1: return ".1";
|
||||
default: return ".n";
|
||||
}
|
||||
} catch (err :ArgumentError) {
|
||||
// fall out
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the translation for the specified compound message key. A
|
||||
* compound key contains the message key followed by a tab separated
|
||||
* list of message arguments which will be subsituted into the
|
||||
* translation string.
|
||||
*
|
||||
* <p> See {@link MessageFormat} for more information on how the
|
||||
* substitution is performed. If a translation message does not exist
|
||||
* for the specified key, an error is logged and the key itself (plus
|
||||
* the arguments) is returned so that the caller need not worry about
|
||||
* handling a null response.
|
||||
*/
|
||||
public function xlate (compoundKey :String) :String
|
||||
{
|
||||
// if this is a qualified key, we need to pass the buck to the
|
||||
// appropriate message bundle; we have to do it here because we
|
||||
// want the compound arguments of this key to be translated in the
|
||||
// context of the containing message bundle qualification
|
||||
if (compoundKey.indexOf(QUAL_PREFIX) == 0) {
|
||||
var qbundle :MessageBundle = _msgmgr.getBundle(
|
||||
getBundle(compoundKey));
|
||||
return qbundle.xlate(getUnqualifiedKey(compoundKey));
|
||||
}
|
||||
|
||||
// to be more efficient about creating unnecessary objects, we
|
||||
// do some checking before splitting
|
||||
var tidx :int = compoundKey.indexOf("|");
|
||||
if (tidx == -1) {
|
||||
return get(compoundKey);
|
||||
|
||||
} else {
|
||||
var key :String = compoundKey.substring(0, tidx);
|
||||
var argstr :String = compoundKey.substring(tidx+1);
|
||||
var args :Array = argstr.split("|");
|
||||
// unescape and translate the arguments
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
// if the argument is tainted, do no further translation
|
||||
// (it might contain |s or other fun stuff)
|
||||
if (args[ii].indexOf(TAINT_CHAR) == 0) {
|
||||
args[ii] = unescape(args[ii].substring(1));
|
||||
} else {
|
||||
args[ii] = xlate(unescape(args[ii]));
|
||||
}
|
||||
}
|
||||
return get(key, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to "taint" any string that has been entered by an entity
|
||||
* outside the application so that the translation code knows not to
|
||||
* attempt to translate this string when doing recursive translations
|
||||
* (see {@link #xlate}).
|
||||
*/
|
||||
public static function taint (text :Object) :String
|
||||
{
|
||||
return TAINT_CHAR + text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Composes a message key with an array of arguments. The message can
|
||||
* subsequently be translated in a single call using {@link #xlate}.
|
||||
*/
|
||||
public static function compose (key :String, ... args) :String
|
||||
{
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
var buf :StringBuilder = new StringBuilder();
|
||||
buf.append(key, "|");
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
if (ii > 0) {
|
||||
buf.append("|");
|
||||
}
|
||||
var arg :String = String(args[ii]);
|
||||
for (var p :int = 0; p < arg.length; p++) {
|
||||
var ch :String = arg.charAt(p);
|
||||
if (ch == "|") {
|
||||
buf.append("\\!");
|
||||
} else if (ch == "\\") {
|
||||
buf.append("\\\\");
|
||||
} else {
|
||||
buf.append(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for calling {@link #compose(String,Object[])}
|
||||
* with a single argument that will be automatically tainted (see
|
||||
* {@link #taint}).
|
||||
*/
|
||||
public static function tcompose (key :String, ... args) :String
|
||||
{
|
||||
args = Util.unfuckVarargs(args);
|
||||
|
||||
for (var ii :int = 0; ii < args.length; ii++) {
|
||||
args[ii] = taint(args[ii]);
|
||||
}
|
||||
return compose(key, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fully qualified message key which, when translated by
|
||||
* some other bundle, will know to resolve and utilize the supplied
|
||||
* bundle to translate this particular key.
|
||||
*/
|
||||
public static function qualify (bundle :String, key :String) :String
|
||||
{
|
||||
if (bundle.indexOf(QUAL_PREFIX) != -1 ||
|
||||
bundle.indexOf(QUAL_SEP) != -1) {
|
||||
throw new Error("Message bundle may not contain " + QUAL_PREFIX +
|
||||
" or " + QUAL_SEP);
|
||||
}
|
||||
|
||||
return QUAL_PREFIX + bundle + QUAL_SEP + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bundle name from a fully qualified message key.
|
||||
*
|
||||
* @see #qualify
|
||||
*/
|
||||
public static function getBundle (qualifiedKey :String) :String
|
||||
{
|
||||
if (qualifiedKey.indexOf(QUAL_PREFIX) != 0) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
var qsidx :int = qualifiedKey.indexOf(QUAL_SEP);
|
||||
if (qsidx == -1) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a valid fully qualified key.");
|
||||
}
|
||||
|
||||
return qualifiedKey.substring(QUAL_PREFIX.length, qsidx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unqualified portion of the key from a fully qualified
|
||||
* message key.
|
||||
*
|
||||
* @see #qualify
|
||||
*/
|
||||
public static function getUnqualifiedKey (qualifiedKey :String) :String
|
||||
{
|
||||
if (qualifiedKey.indexOf(QUAL_PREFIX) != 0) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
var qsidx :int = qualifiedKey.indexOf(QUAL_SEP);
|
||||
if (qsidx == -1) {
|
||||
throw new Error(qualifiedKey +
|
||||
" is not a fully qualified message key.");
|
||||
}
|
||||
return qualifiedKey.substring(qsidx + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unescapes characters that are escaped in a call to compose.
|
||||
*/
|
||||
public static function unescape (val :String) :String
|
||||
{
|
||||
var bsidx :int = val.indexOf("\\");
|
||||
if (bsidx == -1) {
|
||||
return val;
|
||||
}
|
||||
|
||||
var buf :StringBuilder = new StringBuilder();
|
||||
for (var ii :int = 0; ii < val.length; ii++) {
|
||||
var ch :String = val.charAt(ii);
|
||||
if (ch != "\\" || ii == val.length-1) {
|
||||
buf.append(ch);
|
||||
} else {
|
||||
// look at the next character
|
||||
ch = val.charAt(++ii);
|
||||
buf.append((ch == "!") ? "|" : ch);
|
||||
}
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** The message manager via whom we'll resolve fully qualified
|
||||
* translation strings. */
|
||||
protected var _msgmgr :MessageManager;
|
||||
|
||||
/** The path that identifies the resource bundle we are using to
|
||||
* obtain our messages. */
|
||||
protected var _path :String;
|
||||
|
||||
/** Our parent bundle if we're not the global bundle. */
|
||||
protected var _parent :MessageBundle;
|
||||
|
||||
protected static const TAINT_CHAR :String = "~";
|
||||
protected static const QUAL_PREFIX :String = "%";
|
||||
protected static const QUAL_SEP :String = ":";
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import mx.resources.IResourceBundle;
|
||||
import mx.resources.Locale;
|
||||
import mx.resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* The message manager provides a thin wrapper around Java's built-in
|
||||
* localization support, supporting a policy of dividing up localization
|
||||
* resources into logical units, all of the translations for which are
|
||||
* contained in a single messages file.
|
||||
*
|
||||
* <p> The message manager assumes that the locale remains constant for
|
||||
* the duration of its operation. If the locale were to change during the
|
||||
* operation of the client, a call to {@link #setLocale} should be made to
|
||||
* inform the message manager of the new locale (which will clear the
|
||||
* message bundle cache).
|
||||
*/
|
||||
public class MessageManager
|
||||
{
|
||||
/** The name of the global resource bundle (which other bundles revert
|
||||
* to if they can't locate a message within themselves). It must be
|
||||
* named <code>global.properties</code> and live at the top of the
|
||||
* bundle hierarchy. */
|
||||
public static const GLOBAL_BUNDLE :String = "global";
|
||||
|
||||
/**
|
||||
* Constructs a message manager with the supplied resource prefix and
|
||||
* the default locale. The prefix will be prepended to the path of all
|
||||
* resource bundles prior to their resolution. For example, if a
|
||||
* prefix of <code>rsrc.messages</code> was provided and a message
|
||||
* bundle with the name <code>game.chess</code> was later requested,
|
||||
* the message manager would attempt to load a resource bundle with
|
||||
* the path <code>rsrc.messages.game.chess</code> and would eventually
|
||||
* search for a file in the classpath with the path
|
||||
* <code>rsrc/messages/game/chess.properties</code>.
|
||||
*
|
||||
* <p> See the documentation for {@link
|
||||
* ResourceBundle#getBundle(String,Locale,ClassLoader)} for a more
|
||||
* detailed explanation of how resource bundle paths are resolved.
|
||||
*/
|
||||
public function MessageManager ()
|
||||
{
|
||||
// load up the global bundle
|
||||
_global = getBundle(GLOBAL_BUNDLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the message bundle for the specified path. If no bundle can
|
||||
* be located with the specified path, a special bundle is returned
|
||||
* that returns the untranslated message identifiers instead of an
|
||||
* associated translation. This is done so that error code to handle a
|
||||
* failed bundle load need not be replicated wherever bundles are
|
||||
* used. Instead an error will be logged and the requesting service
|
||||
* can continue to function in an impaired state.
|
||||
*/
|
||||
public function getBundle (path :String) :MessageBundle
|
||||
{
|
||||
// first look in the cache
|
||||
var bundle :MessageBundle = (_cache.get(path) as MessageBundle);
|
||||
if (bundle != null) {
|
||||
return bundle;
|
||||
}
|
||||
|
||||
// see if we should use a custom class for the bundle
|
||||
var mbclass :String = ResourceManager.getInstance().getString(path, MBUNDLE_CLASS_KEY);
|
||||
if (mbclass != null) {
|
||||
try {
|
||||
var clazz :Class = ClassUtil.getClassByName(String(mbclass));
|
||||
bundle = new clazz();
|
||||
} catch (ee :Error) {
|
||||
Log.getLog(this).warning(
|
||||
"Failure instantiating custom message bundle " +
|
||||
"[mbclass=" + mbclass + ", error=" + ee + "].");
|
||||
}
|
||||
}
|
||||
// if there was no custom class, or we failed to instantiate the
|
||||
// custom class, use a standard message bundle
|
||||
if (bundle == null) {
|
||||
bundle = new MessageBundle();
|
||||
}
|
||||
|
||||
// initialize our message bundle, cache it and return it (if we
|
||||
// couldn't resolve the bundle, the message bundle will cope with
|
||||
// it's null resource bundle)
|
||||
bundle.init(this, path, _global);
|
||||
_cache.put(path, bundle);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/** The locale for which we're obtaining message bundles. */
|
||||
protected var _locale :String;
|
||||
|
||||
/** A cache of instantiated message bundles. */
|
||||
protected var _cache :HashMap = new HashMap();
|
||||
|
||||
/** Our top-level message bundle, from which others obtain messages if
|
||||
* they can't find them within themselves. */
|
||||
protected var _global :MessageBundle;
|
||||
|
||||
/** A key that can contain the classname of a custom message bundle
|
||||
* class to be used to handle messages for a particular bundle. */
|
||||
protected static const MBUNDLE_CLASS_KEY :String = "msgbundle_class";
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.TimerEvent;
|
||||
|
||||
import flash.utils.Timer;
|
||||
|
||||
/**
|
||||
* A simple mechanism for queueing functions to be called on the next frame.
|
||||
* Similar to UIComponent's callLater, only flex-free.
|
||||
*/
|
||||
public class MethodQueue
|
||||
{
|
||||
/**
|
||||
* Call the specified method at the entry to the next frame.
|
||||
*/
|
||||
public static function callLater (fn :Function, args :Array = null) :void
|
||||
{
|
||||
_methodQueue.push([fn, args]);
|
||||
_t.start(); // starts the timer if it's not already running
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a timer event: call any queued functions.
|
||||
*/
|
||||
protected static function handleTimer (event :TimerEvent) :void
|
||||
{
|
||||
// swap out the working set
|
||||
var methods :Array = _methodQueue;
|
||||
_methodQueue = [];
|
||||
|
||||
// safely call each function
|
||||
for each (var arr :Array in methods) {
|
||||
var fn :Function = (arr[0] as Function);
|
||||
var args :Array = (arr[1] as Array);
|
||||
try {
|
||||
fn.apply(null, args);
|
||||
|
||||
} catch (e :Error) {
|
||||
Log.getLog(MethodQueue).warning(
|
||||
"Error calling deferred method", "fn", fn, "args", args, e);
|
||||
}
|
||||
}
|
||||
|
||||
// If no new functions were added while we were calling the current set,
|
||||
// then stop firing
|
||||
if (_methodQueue.length == 0) {
|
||||
_t.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/** A timer that will fire as quickly as possible. */
|
||||
protected static var _t :Timer = new Timer(1);
|
||||
|
||||
/** The currently queued functions. */
|
||||
protected static var _methodQueue :Array = [];
|
||||
|
||||
// a bit of static initialization
|
||||
_t.addEventListener(TimerEvent.TIMER, handleTimer);
|
||||
}
|
||||
}
|
||||
@@ -1,344 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.display.Loader;
|
||||
import flash.display.LoaderInfo;
|
||||
|
||||
import flash.events.AsyncErrorEvent;
|
||||
import flash.events.ErrorEvent;
|
||||
import flash.events.Event;
|
||||
import flash.events.IEventDispatcher;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.SecurityErrorEvent;
|
||||
|
||||
import flash.net.URLRequest;
|
||||
|
||||
import flash.system.ApplicationDomain;
|
||||
import flash.system.LoaderContext;
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* Easy loader for many things, including managing multiple downloads.
|
||||
* More documentation coming.
|
||||
*/
|
||||
public class MultiLoader
|
||||
{
|
||||
/**
|
||||
* Load one or more sources and return DisplayObjects.
|
||||
*
|
||||
* @param sources an Array, Dictionary, or Object containing sources as values, or a single
|
||||
* source value. The sources may be Strings (representing urls), URLRequests, ByteArrays,
|
||||
* or a Class that can be instantiated to become a URLRequest or ByteArray. Note that
|
||||
* the format of your sources Object dictates the format of the return Object.
|
||||
* @param completeCallback the function to call when complete. The signature should be:
|
||||
* <code>function (value :Object) :void</code>. Note that the structure of the return Object
|
||||
* is dictated by the sources parameter. If you pass in an Array, you get your results
|
||||
* in an Array. If you use a Dictionary or Object, the results will be returned as the same,
|
||||
* with the same keys used in sources now pointing to the results. If your sources parameter
|
||||
* was just a single source (like a String) then the result will just be a single result,
|
||||
* like a DisplayObject. Each result will be a DisplayObject or an Error
|
||||
* describing the problem.
|
||||
* @param forEach if true, each value or error will be returned as soon as possible. The values
|
||||
* or errors will be returned directly to the completeCallback. Any keys are lost, so you
|
||||
* probably only want to use this with an Array sources.
|
||||
* @param appDom the ApplicationDomain in which to load the contents, or null to specify
|
||||
* that it should load in a child of the current ApplicationDomain.
|
||||
*
|
||||
* @example Load one embed, add it as a child.
|
||||
* <listing version="3.0">
|
||||
* MultiLoader.getContents(EMBED_CONSTANT, addChild);
|
||||
* </listing>
|
||||
*
|
||||
* @example Load 3 embeds, add them as children.
|
||||
* <listing version="3.0">
|
||||
* MultiLoader.getContents([EMBED1, EMBED2, EMBED3], addChild, true);
|
||||
* </listing>
|
||||
*
|
||||
* @example Load multiple URLs, have the contents returned to the result function one at
|
||||
* a time.
|
||||
* <listing version="3.0">
|
||||
* function handleComplete (result :Object) :void {
|
||||
* // process a result here. Result may be a DisplayObject or an Error.
|
||||
* };
|
||||
*
|
||||
* var obj :Object = {
|
||||
* key1: "http://somehost.com/someImage.gif",
|
||||
* key2: "http://somehost.com/someOtherImage.gif"
|
||||
* };
|
||||
*
|
||||
* MultiLoader.getContents(obj, handleComplete, true);
|
||||
* </listing>
|
||||
*
|
||||
* @example Load 3 embeds, wait to handle them until they're all loaded.
|
||||
* <listing version="3.0">
|
||||
* function handleComplete (results :Array) :void {
|
||||
* // process results here
|
||||
* };
|
||||
*
|
||||
* MultiLoader.getContents([EMBED1, EMBED2, EMBED3], handleComplete);
|
||||
* </listing>
|
||||
*/
|
||||
public static function getContents (
|
||||
sources :Object, completeCallback :Function, forEach :Boolean = false,
|
||||
appDom :ApplicationDomain = null) :void
|
||||
{
|
||||
var complete :Function = function (retval :Object) :void {
|
||||
completeCallback(processProperty(retval, Loader, "content"));
|
||||
};
|
||||
getLoaders(sources, complete, forEach, appDom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exactly like getContents() only it returns the Loader objects rather than their contents.
|
||||
*
|
||||
* @example Advanced usage: Loading classes.
|
||||
* <listing version="3.0">
|
||||
* // A holder for new classes, created as a child of the system domain.
|
||||
* var appDom :ApplicationDomain = new ApplicationDomain(null);
|
||||
* <br/>
|
||||
* function handleComplete (results :Object) :void {
|
||||
* // now we can retrieve classes
|
||||
* var clazz :Class = appDom.getDefinition("com.package.SomeClass") as Class;
|
||||
* }
|
||||
* <br/>
|
||||
* // load all the classes contained in the specified sources
|
||||
* MultiLoader.getLoaders([EMBED, "http://site.com/pack.swf"], handleComplete, false, appDom);
|
||||
* <br/>
|
||||
* [Embed(source="resource.swf", mimeType="application/octet-stream")]
|
||||
* private static const EMBED :Class;
|
||||
* </listing>
|
||||
*
|
||||
* @see getContents()
|
||||
*/
|
||||
public static function getLoaders (
|
||||
sources :Object, completeCallback :Function, forEach :Boolean = false,
|
||||
appDom :ApplicationDomain = null) :void
|
||||
{
|
||||
var generator :Function = function (source :*) :Object {
|
||||
// first transform common sources to their more useful nature
|
||||
if (source is String) {
|
||||
source = new URLRequest(String(source));
|
||||
} else if (source is Class) {
|
||||
// it's probably a ByteArray from an Embed, but don't cast it
|
||||
source = new (source as Class)();
|
||||
}
|
||||
var l :Loader = new Loader();
|
||||
var lc :LoaderContext = new LoaderContext(false, appDom);
|
||||
try {
|
||||
Object(lc)["allowLoadBytesCodeExecution"] = true; // AIR compatibility
|
||||
} catch (err :Error) { /* We must not be in AIR! */ }
|
||||
// now we only need handle the two cases
|
||||
if (source is URLRequest) {
|
||||
l.load(URLRequest(source), lc);
|
||||
} else if (source is ByteArray) {
|
||||
l.loadBytes(ByteArray(source), lc);
|
||||
} else {
|
||||
return new Error("Unknown source: " + source);
|
||||
}
|
||||
return l.contentLoaderInfo;
|
||||
};
|
||||
|
||||
var complete :Function = function (retval :Object) :void {
|
||||
completeCallback(processProperty(retval, LoaderInfo, "loader"));
|
||||
};
|
||||
|
||||
new MultiLoader(sources, complete, generator, forEach);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads classes into the specified ApplicationDomain.
|
||||
* The complete callback receives either the same ApplicationDomain or no arguments.
|
||||
*/
|
||||
public static function loadClasses (
|
||||
sources :Object, appDom :ApplicationDomain, completeCallback :Function) :void
|
||||
{
|
||||
var complete :Function = function (retval :Object) :void {
|
||||
switch (completeCallback.length) {
|
||||
default:
|
||||
completeCallback();
|
||||
break;
|
||||
case 1:
|
||||
completeCallback(appDom);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
getLoaders(sources, complete, false, appDom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coordinate loading some asynchronous objects.
|
||||
*
|
||||
* @param sources An Array, Dictionary, or Object of sources, or just a single source.
|
||||
* @param completeCallack the function to call when complete.
|
||||
* @param generatorFunciton a function to call to generate the IEventDispatchers, or
|
||||
* null if the source values are already ready to go.
|
||||
* @param forEach whether to call the completeCallback for each source, or all-at-once at
|
||||
* the end. If forEach is used, keys will never be returned.
|
||||
* @param isCompleteCheckFn a function to attempt to call on the dispatcher to see if
|
||||
* it's already complete after generation.
|
||||
* @param errorTypes an Array of event types that will be dispatched by the loader.
|
||||
* If unspecifed, all the normal error event types are used.
|
||||
* @param completeType, the event complete type. If unspecifed @default Event.COMPLETE.
|
||||
*/
|
||||
public function MultiLoader (
|
||||
sources :Object, completeCallback :Function, generatorFn :Function = null,
|
||||
forEach :Boolean = false, isCompleteCheckFn :String = null,
|
||||
errorTypes :Array = null, completeType :String = null)
|
||||
{
|
||||
if (errorTypes == null) {
|
||||
errorTypes = [ ErrorEvent.ERROR, AsyncErrorEvent.ASYNC_ERROR,
|
||||
IOErrorEvent.IO_ERROR, SecurityErrorEvent.SECURITY_ERROR ];
|
||||
}
|
||||
if (completeType == null) {
|
||||
completeType = Event.COMPLETE;
|
||||
}
|
||||
|
||||
_complete = completeCallback;
|
||||
_forEach = forEach;
|
||||
|
||||
var endCheckKey :* = null;
|
||||
if (sources is Array) {
|
||||
_result = new Array();
|
||||
|
||||
} else if (sources is Dictionary) {
|
||||
_result = new Dictionary();
|
||||
|
||||
} else {
|
||||
_result = new Object();
|
||||
if (!Util.isPlainObject(sources)) {
|
||||
// stash the singleton source
|
||||
sources = { singleton_key: sources };
|
||||
endCheckKey = "singleton_key";
|
||||
}
|
||||
}
|
||||
|
||||
for (var key :* in sources) {
|
||||
var val :Object = sources[key];
|
||||
if (generatorFn != null) {
|
||||
try {
|
||||
val = (val is Array) ? generatorFn.apply(null, val as Array)
|
||||
: generatorFn(val);
|
||||
} catch (err :Error) {
|
||||
val = err;
|
||||
}
|
||||
}
|
||||
_result[key] = val;
|
||||
if ((val is IEventDispatcher) &&
|
||||
(isCompleteCheckFn == null || !val[isCompleteCheckFn]())) {
|
||||
var ed :IEventDispatcher = IEventDispatcher(val);
|
||||
_remaining++;
|
||||
_targetsToKeys[ed] = key;
|
||||
ed.addEventListener(completeType, handleComplete);
|
||||
for each (var type :String in errorTypes) {
|
||||
ed.addEventListener(type, handleError);
|
||||
}
|
||||
} else if (_forEach) {
|
||||
checkReport(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (!_forEach) {
|
||||
checkReport(endCheckKey);
|
||||
}
|
||||
|
||||
// if we're not done at this point, keep a reference to this loader
|
||||
if (_remaining > 0) {
|
||||
_activeMultiLoaders[this] = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function handleError (event :ErrorEvent) :void
|
||||
{
|
||||
_result[_targetsToKeys[event.target]] = new Error(event.text) // , event.errorID); ???
|
||||
handleComplete(event); // the rest is the same as complete
|
||||
}
|
||||
|
||||
protected function handleComplete (event :Event) :void
|
||||
{
|
||||
_remaining--;
|
||||
checkReport(_targetsToKeys[event.target]);
|
||||
}
|
||||
|
||||
protected function checkReport (key :*) :void
|
||||
{
|
||||
if (!_forEach && _remaining > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var thisResult :Object = (_forEach || (key === "singleton_key")) ? _result[key] : _result;
|
||||
try {
|
||||
_complete(thisResult);
|
||||
} catch (err :Error) {
|
||||
trace("MultiLoader: Error calling completeCallback [result=" + thisResult + "].");
|
||||
trace("Cause: " + err.getStackTrace());
|
||||
}
|
||||
|
||||
if (_forEach) {
|
||||
delete _result[key]; // free the loaded object to assist gc
|
||||
}
|
||||
|
||||
// If we're all done, remove the static reference to this loader.
|
||||
// Note that this could be called in for-each mode if we haven't yet started to load
|
||||
// something asynchronously but have come across an Error or an already-completed load.
|
||||
// That's ok, as this will just end up deleting a reference that doesn't exist, and if
|
||||
// necessary the reference will still be added at the end of the constructor.
|
||||
if (_remaining == 0) {
|
||||
delete _activeMultiLoaders[this];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method used in this class.
|
||||
*/
|
||||
protected static function processProperty (
|
||||
retval :Object, testClass :Class, prop :String) :Object
|
||||
{
|
||||
if (retval is testClass) {
|
||||
retval = retval[prop];
|
||||
} else {
|
||||
for (var key :* in retval) {
|
||||
var o :Object = retval[key];
|
||||
if (o is testClass) {
|
||||
retval[key] = o[prop];
|
||||
} // else keep it the same
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
protected var _complete :Function;
|
||||
|
||||
protected var _result :Object;
|
||||
|
||||
protected var _targetsToKeys :Dictionary = new Dictionary(true);
|
||||
|
||||
protected var _forEach :Boolean;
|
||||
|
||||
protected var _remaining :int = 0;
|
||||
|
||||
protected static const _activeMultiLoaders :Dictionary = new Dictionary();
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.net.URLRequest;
|
||||
//import flash.net.navigateToURL; // function import
|
||||
|
||||
public class NetUtil
|
||||
{
|
||||
/**
|
||||
* Convenience method to load a web page in the browser window without
|
||||
* having to worry about SecurityErrors in various conditions.
|
||||
*
|
||||
* @param url a String or a URLRequest.
|
||||
* @param preferredWindow the browser tab/window identifier in which to load. If you
|
||||
* specify a non-null window and it causes a security error, the request is retried with null.
|
||||
*
|
||||
* @return true if the url was able to be loaded.
|
||||
*/
|
||||
public static function navigateToURL (url :*, preferredWindow :String = "_self") :Boolean
|
||||
{
|
||||
var ureq :URLRequest = (url is URLRequest) ? URLRequest(url) : new URLRequest(String(url));
|
||||
while (true) {
|
||||
try {
|
||||
flash.net.navigateToURL(ureq, preferredWindow);
|
||||
return true;
|
||||
} catch (err :SecurityError) {
|
||||
if (preferredWindow != null) {
|
||||
preferredWindow = null; // try again with no preferred window
|
||||
|
||||
} else {
|
||||
Log.getLog(NetUtil).warning("Unable to navigate to URL [e=" + err + "].");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // failure!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import com.threerings.util.Map;
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* An implemention of Map that uses a Dictionary internally for storage. Any Object (and null)
|
||||
* may be used as a key with no loss in efficiency.
|
||||
*/
|
||||
public class ObjectMap
|
||||
implements Map
|
||||
{
|
||||
/** @inheritDoc */
|
||||
public function get (key :Object) :*
|
||||
{
|
||||
return _dict[key];
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function put (key :Object, value :Object) :*
|
||||
{
|
||||
var oldVal :* = _dict[key];
|
||||
_dict[key] = value;
|
||||
if (oldVal === undefined) {
|
||||
_size++;
|
||||
}
|
||||
|
||||
return oldVal;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function remove (key :Object) :*
|
||||
{
|
||||
var oldVal :* = _dict[key];
|
||||
if (oldVal !== undefined) {
|
||||
delete _dict[key];
|
||||
_size--;
|
||||
}
|
||||
|
||||
return oldVal;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function clear () :void
|
||||
{
|
||||
_dict = new Dictionary();
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function containsKey (key :Object) :Boolean
|
||||
{
|
||||
return (get(key) !== undefined);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function size () :int
|
||||
{
|
||||
return _size;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function isEmpty () :Boolean
|
||||
{
|
||||
return (_size == 0);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function keys () :Array
|
||||
{
|
||||
var keys :Array = [];
|
||||
forEach0(function (k :*, v :*) :void {
|
||||
keys.push(k);
|
||||
});
|
||||
return keys;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function values () :Array
|
||||
{
|
||||
var vals :Array = [];
|
||||
forEach0(function (k :*, v :*) :void {
|
||||
vals.push(v);
|
||||
});
|
||||
return vals;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function forEach (fn :Function) :void
|
||||
{
|
||||
forEach0(fn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal forEach.
|
||||
* @private
|
||||
*/
|
||||
protected function forEach0 (fn :Function) :void
|
||||
{
|
||||
for (var key :Object in _dict) {
|
||||
fn(key, _dict[key]);
|
||||
}
|
||||
}
|
||||
|
||||
protected var _dict :Dictionary = new Dictionary();
|
||||
protected var _size :int = 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import com.threerings.util.ObjectMap;
|
||||
import com.threerings.util.Set;
|
||||
|
||||
public class ObjectSet
|
||||
implements Set
|
||||
{
|
||||
/** @inheritDoc */
|
||||
public function add (o :Object) :Boolean
|
||||
{
|
||||
return (_objectMap.put(o, null) === undefined);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function remove (o :Object) :Boolean
|
||||
{
|
||||
return (_objectMap.remove(o) !== undefined);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function clear () :void
|
||||
{
|
||||
_objectMap.clear();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function contains (o :Object) :Boolean
|
||||
{
|
||||
return _objectMap.containsKey(o);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function size () :int
|
||||
{
|
||||
return _objectMap.size();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function isEmpty () :Boolean
|
||||
{
|
||||
return _objectMap.isEmpty();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function toArray () :Array
|
||||
{
|
||||
return _objectMap.keys();
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
public function forEach (callback :Function) :void
|
||||
{
|
||||
_objectMap.forEach(function (key :Object, ...ignored) :void {
|
||||
callback(key);
|
||||
});
|
||||
}
|
||||
|
||||
protected var _objectMap :ObjectMap = new ObjectMap();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public class ObserverList
|
||||
{
|
||||
/** A notification ordering policy indicating that the observers
|
||||
* should be notified in the order they were added and that the
|
||||
* notification should be done on a snapshot of the array. */
|
||||
public static const SAFE_IN_ORDER_NOTIFY :int = 1;
|
||||
|
||||
/** A notification ordering policy wherein the observers are notified
|
||||
* last to first so that they can be removed during the notification
|
||||
* process and new observers added will not inadvertently be notified
|
||||
* as well, but no copy of the observer list need be made. This will
|
||||
* not work if observers are added or removed from arbitrary positions
|
||||
* in the list during a notification call. */
|
||||
public static const FAST_UNSAFE_NOTIFY :int = 2;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function ObserverList (notifyPolicy :int = 2)
|
||||
{
|
||||
_notifyPolicy = notifyPolicy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an observer to this list.
|
||||
*
|
||||
* @param index the index at which to add the observer, or -1 for the end.
|
||||
*/
|
||||
public function add (observer :Object, index :int = -1) :void
|
||||
{
|
||||
if (!ArrayUtil.contains(_list, observer)) {
|
||||
_list.splice(index, 0, observer); // -1 to splice means "end"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the size of the list.
|
||||
*/
|
||||
public function size () :int
|
||||
{
|
||||
return _list.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an observer from this list.
|
||||
*/
|
||||
public function remove (observer :Object) :void
|
||||
{
|
||||
ArrayUtil.removeFirst(_list, observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply some operation to all observers.
|
||||
* The function to be passed in should expect one argument and either
|
||||
* return void or a Boolean. If returning a Boolean, returning false
|
||||
* indicates that the observer should be removed from the list.
|
||||
*/
|
||||
public function apply (func :Function) :void
|
||||
{
|
||||
var list :Array = _list;
|
||||
if (_notifyPolicy == SAFE_IN_ORDER_NOTIFY) {
|
||||
list = list.concat(); // make a duplicate
|
||||
list.reverse(); // reverse it so that we start with earlier obs
|
||||
}
|
||||
for (var ii :int = list.length-1; ii >= 0; ii--) {
|
||||
try {
|
||||
var result :* = func(list[ii]);
|
||||
if (result !== undefined && !Boolean(result)) {
|
||||
// remove it if directed to do so
|
||||
remove(list[ii]);
|
||||
}
|
||||
} catch (err :Error) {
|
||||
var log :Log = Log.getLog(this);
|
||||
log.warning("ObserverOp choked during notification.");
|
||||
log.logStackTrace(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Our notification policy. */
|
||||
protected var _notifyPolicy :int;
|
||||
|
||||
/** The actual list of observers. */
|
||||
protected var _list :Array = new Array();
|
||||
}
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.display.DisplayObject;
|
||||
import flash.display.LoaderInfo;
|
||||
|
||||
import flash.events.Event;
|
||||
import flash.events.IOErrorEvent;
|
||||
|
||||
import flash.net.URLLoader;
|
||||
import flash.net.URLRequest;
|
||||
|
||||
/**
|
||||
* A utility for loading parameters from an XML file when run from
|
||||
* the local filesystem.
|
||||
*
|
||||
* The file "parameters.xml" should reside in the current directory and contains:
|
||||
* <parameters>
|
||||
* <param name="name1" value="val1"/>
|
||||
* <param name="name2" value="val2"/>
|
||||
* </parameters>
|
||||
*/
|
||||
public class ParameterUtil
|
||||
{
|
||||
/**
|
||||
* Get the parameters.
|
||||
* Note: the callback function may be called prior to this method
|
||||
* returning.
|
||||
*/
|
||||
public static function getParameters (disp :DisplayObject, callback :Function) :void
|
||||
{
|
||||
return getInfoParameters(disp.root.loaderInfo, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters.
|
||||
* Note: the callback function may be called prior to this method
|
||||
* returning.
|
||||
*/
|
||||
public static function getInfoParameters (loaderInfo :LoaderInfo, callback :Function) :void
|
||||
{
|
||||
var url :String = loaderInfo.url;
|
||||
|
||||
// ensure that it's initialized...
|
||||
if (url == null) {
|
||||
// Create a function to wait until the loaded object is initialized
|
||||
var initWaiter :Function = function (event :Event) :void {
|
||||
loaderInfo.removeEventListener(Event.INIT, initWaiter);
|
||||
if (loaderInfo.url != null) {
|
||||
// re-call
|
||||
getInfoParameters(loaderInfo, callback);
|
||||
|
||||
} else {
|
||||
// url is still null, don't infinite loop
|
||||
logWarning("Unable to determine url, bailing");
|
||||
callback(loaderInfo.parameters);
|
||||
}
|
||||
}; // end- initWaiter function
|
||||
|
||||
// and wait.
|
||||
loaderInfo.addEventListener(Event.INIT, initWaiter);
|
||||
return;
|
||||
}
|
||||
|
||||
// Simply use the parameters in the loaderInfo if we were not
|
||||
// loaded from a file.
|
||||
if (0 != url.indexOf("file:")) {
|
||||
callback(loaderInfo.parameters);
|
||||
return;
|
||||
}
|
||||
// else, let's try loading parameters.xml
|
||||
|
||||
var dex :int = url.lastIndexOf("/");
|
||||
var paramUrl :String;
|
||||
if (dex == -1) {
|
||||
paramUrl = "file:";
|
||||
} else {
|
||||
paramUrl = url.substring(0, dex + 1);
|
||||
}
|
||||
paramUrl += "parameters.xml";
|
||||
Log.getLog(ParameterUtil).info("Trying to load parameters from " + paramUrl);
|
||||
|
||||
var loader :URLLoader = new URLLoader();
|
||||
loader.addEventListener(IOErrorEvent.IO_ERROR,
|
||||
function (event :Event) :void {
|
||||
logWarning("Error loading params: " + event);
|
||||
callback(loaderInfo.parameters);
|
||||
}
|
||||
);
|
||||
loader.addEventListener(Event.COMPLETE,
|
||||
function (event :Event) :void {
|
||||
var data :XML = Util.newXML(loader.data);
|
||||
var params :Object = {};
|
||||
for each (var param :XML in data..param) {
|
||||
params[param.@name] = String(param.@value);
|
||||
}
|
||||
callback(params);
|
||||
}
|
||||
);
|
||||
loader.load(new URLRequest(paramUrl));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to log a warning, since we don't keep around
|
||||
* a Log instance.
|
||||
*/
|
||||
protected static function logWarning (msg :String) :void
|
||||
{
|
||||
Log.getLog(ParameterUtil).warning(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Predicates suitable for Array.filter() and other needs.
|
||||
*/
|
||||
public class Predicates
|
||||
{
|
||||
/**
|
||||
* Return a predicate that tests for null (or undefined) items.
|
||||
*/
|
||||
public static function isNull () :Function
|
||||
{
|
||||
//return not(notNull());
|
||||
return function (item :*, ... _) :Boolean {
|
||||
return (item == null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that tests for items that are not null (or undefined).
|
||||
*/
|
||||
public static function notNull () :Function
|
||||
{
|
||||
return function (item :*, ... _) :Boolean {
|
||||
return (item != null);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that tests for items that are "is" the specified class.
|
||||
*/
|
||||
public static function instanceOf (clazz :Class) :Function
|
||||
{
|
||||
return function (item :*, ... _) :Boolean {
|
||||
return (item is clazz);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that is the negation of the specified predicate.
|
||||
*/
|
||||
public static function not (pred :Function) :Function
|
||||
{
|
||||
return function (... args) :Boolean {
|
||||
return !pred.apply(null, args);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that is true if all the specified predicate Functions are true
|
||||
* for any item.
|
||||
*/
|
||||
public static function and (... predicates) :Function
|
||||
{
|
||||
return function (... args) :Boolean {
|
||||
for each (var pred :Function in predicates) {
|
||||
if (!pred.apply(null, args)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a predicate that is true if any of the specified predicate Functions are true
|
||||
* for any item.
|
||||
*/
|
||||
public static function or (... predicates) :Function
|
||||
{
|
||||
return function (... args) :Boolean {
|
||||
for each (var pred :Function in predicates) {
|
||||
if (pred.apply(null, args)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,148 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util
|
||||
{
|
||||
|
||||
/**
|
||||
* A seedable pseudorandom generator of the Mersenne Twister variety, with an extremely
|
||||
* long period. Note that this is not a cryptographically sound generator.
|
||||
* This implementation is based on one found at --
|
||||
* http://onegame.bona.jp/tips/mersennetwister.html
|
||||
* An explanation of the algorithm can be found at --
|
||||
* http://en.wikipedia.org/wiki/Mersenne_twister
|
||||
*/
|
||||
public class Random
|
||||
{
|
||||
/**
|
||||
* Creates a pseudo random number generation.
|
||||
*
|
||||
* @param seed a seed of 0 will randomly seed the generator, anything
|
||||
* other than 0 will create a generator with the specified seed.
|
||||
*/
|
||||
public function Random (seed :uint = 0)
|
||||
{
|
||||
if (seed == 0) {
|
||||
seed = uint(seedUniquifier++ + uint(Math.random() * 4294967295));
|
||||
}
|
||||
x = new Array();
|
||||
setSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the seed of the generator. This will result in the same generator
|
||||
* sequence of values as a new generator created with the specified seed.
|
||||
*/
|
||||
public function setSeed (seed :uint) :void
|
||||
{
|
||||
x[0] = seed;
|
||||
for (var i :int = 1; i < N; i++) {
|
||||
x[i] = imul(1812433253, x[i - 1] ^ (x[i - 1] >>> 30)) + i;
|
||||
x[i] &= 0xffffffff;
|
||||
}
|
||||
p = 0;
|
||||
q = 1;
|
||||
r = M;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the an int value n where 0 <= value < n.
|
||||
*
|
||||
* @param n the range to return. If this is set to 0 it will return a
|
||||
* random integer value. Anything less than 0 will thrown an error.
|
||||
*/
|
||||
public function nextInt (n :int=0) :int
|
||||
{
|
||||
if (n < 0)
|
||||
throw new Error("n must be positive");
|
||||
|
||||
if (n == 0) return int(next(32));
|
||||
|
||||
// Eyeball Sun's documenation of java.util.Random for an explanation
|
||||
// of this while loop.
|
||||
var bits :int, val :int;
|
||||
do {
|
||||
bits = int(next(31));
|
||||
val = bits % n;
|
||||
} while (bits - val + (n - 1) < 0);
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random Boolean value.
|
||||
*/
|
||||
public function nextBoolean () :Boolean
|
||||
{
|
||||
return next(1) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random Number where 0.0 <= value < 1.0.
|
||||
*/
|
||||
public function nextNumber () :Number
|
||||
{
|
||||
return next(32) / 4294967296;
|
||||
}
|
||||
|
||||
protected function next (bits: int) :uint
|
||||
{
|
||||
var y :uint = (x[p] & UPPER_MASK) | (x[q] & LOWER_MASK);
|
||||
x[p] = x[r] ^ (y >>> 1) ^ ((y & 1) * MATRIX_A);
|
||||
y = x[p];
|
||||
|
||||
if (++p == N) { p = 0; }
|
||||
if (++q == N) { q = 0; }
|
||||
if (++r == N) { r = 0; }
|
||||
|
||||
y ^= (y >>> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680;
|
||||
y ^= (y << 15) & 0xefc60000;
|
||||
y ^= (y >>> 18);
|
||||
|
||||
return y >>> (32 - bits);
|
||||
}
|
||||
|
||||
protected function imul (a: Number, b: Number): Number {
|
||||
var al :Number = a & 0xffff;
|
||||
var ah :Number = a >>> 16;
|
||||
var bl :Number = b & 0xffff;
|
||||
var bh :Number = b >>> 16;
|
||||
var ml :Number = al * bl;
|
||||
var mh :Number = ((((ml >>> 16) + al * bh) & 0xffff) + ah * bl) & 0xffff;
|
||||
|
||||
return (mh << 16) | (ml & 0xffff);
|
||||
}
|
||||
|
||||
protected var x :Array;
|
||||
protected var p :int;
|
||||
protected var q :int;
|
||||
protected var r :int;
|
||||
|
||||
protected static var seedUniquifier :uint = 2812526361;
|
||||
|
||||
protected static const N :int = 624;
|
||||
protected static const M :int = 397;
|
||||
protected static const UPPER_MASK :uint = 0x80000000;
|
||||
protected static const LOWER_MASK :uint = 0x7fffffff;
|
||||
protected static const MATRIX_A :uint = 0x9908b0df;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Random Random utilities.
|
||||
*/
|
||||
public class RandomUtil
|
||||
{
|
||||
/**
|
||||
* Return the picked index from a weighted list.
|
||||
*
|
||||
* @param weights an Array containing Numbers, ints, or uints. All values should be
|
||||
* non-negative.
|
||||
* @param a random function that returns (0 <= value < 1), if null then Math.random() will
|
||||
* be used.
|
||||
*/
|
||||
public static function getWeightedIndex (weights :Array, randomFn :Function = null) :int
|
||||
{
|
||||
var sum :Number = 0;
|
||||
for each (var n :Number in weights) {
|
||||
sum += n;
|
||||
}
|
||||
if (sum < 0) {
|
||||
return -1;
|
||||
}
|
||||
var pick :Number = ((randomFn == null) ? Math.random() : randomFn()) * sum;
|
||||
for (var ii :int = 0; ii < weights.length; ii++) {
|
||||
pick -= Number(weights[ii]);
|
||||
if (pick < 0) {
|
||||
return ii;
|
||||
}
|
||||
}
|
||||
|
||||
// since we're dealing with floats, it's possible that a rounding error left us here
|
||||
return 0; // TODO: largest weighted? Re-pick?
|
||||
}
|
||||
|
||||
/**
|
||||
* Picks a random object from the supplied array of values. Even weight is given to all
|
||||
* elements of the array.
|
||||
*
|
||||
* @return a randomly selected item or null if the array is null or of length zero.
|
||||
*/
|
||||
public static function pickRandom (values :Array, randomFn :Function = null) :Object
|
||||
{
|
||||
if (values == null || values.length == 0) {
|
||||
return null;
|
||||
} else {
|
||||
var rand :Number = (randomFn == null) ? Math.random() : randomFn();
|
||||
return values[Math.floor(rand * values.length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,226 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public class RingBuffer
|
||||
{
|
||||
/** Creates a new RingBuffer with the specified capacity. */
|
||||
public function RingBuffer (capacity :uint = 1)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_array.length = _capacity;
|
||||
}
|
||||
|
||||
/** Returns the capacity of the RingBuffer. */
|
||||
public function get capacity () :uint
|
||||
{
|
||||
return _capacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the capacity of the RingBuffer.
|
||||
* If the new capacity is less than the RingBuffer's length,
|
||||
* elements will be removed from the end of the RingBuffer
|
||||
* to accommodate the smaller capacity.
|
||||
*/
|
||||
public function set capacity (newCapacity :uint) :void
|
||||
{
|
||||
// Copy all the elements to a new array.
|
||||
var newArray :Array = new Array();
|
||||
var newLength :uint = Math.min(_length, newCapacity);
|
||||
newArray.length = newCapacity;
|
||||
for (var i :uint = 0; i < newLength; ++i) {
|
||||
newArray[i] = this.at(i);
|
||||
}
|
||||
|
||||
_capacity = newCapacity;
|
||||
_length = newLength;
|
||||
_array = newArray;
|
||||
_firstIndex = 0;
|
||||
}
|
||||
|
||||
/** Returns the number of elements currently stored in the RingBuffer. */
|
||||
public function get length () :uint
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
/** Returns true if the RingBuffer contains 0 elements. */
|
||||
public function get empty () :Boolean
|
||||
{
|
||||
return (0 == _length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified elements to the front of the RingBuffer.
|
||||
* If the RingBuffer's length is equal to its capacity, this
|
||||
* will cause elements to be removed from the back of
|
||||
* the RingBuffer.
|
||||
* Returns the new length of the RingBuffer.
|
||||
*/
|
||||
public function unshift (...args) :uint
|
||||
{
|
||||
for (var i :int = args.length - 1; i >= 0; --i) {
|
||||
var index :uint = (_firstIndex > 0 ? _firstIndex - 1 : _capacity - 1);
|
||||
_array[index] = args[i];
|
||||
_length = Math.min(_length + 1, _capacity);
|
||||
_firstIndex = index;
|
||||
}
|
||||
|
||||
return _length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified elements to the back of the RingBuffer.
|
||||
* If the RingBuffer's length is equal to its capacity, this
|
||||
* will cause a elements to be removed from the front of
|
||||
* the RingBuffer.
|
||||
* Returns the new length of the RingBuffer.
|
||||
*/
|
||||
public function push (...args) :uint
|
||||
{
|
||||
for (var i :uint = 0; i < args.length; ++i) {
|
||||
var index :uint = ((_firstIndex + _length) % _capacity);
|
||||
_array[index] = args[i];
|
||||
_length = Math.min(_length + 1, _capacity);
|
||||
|
||||
// did we overwrite the first index?
|
||||
if (index == _firstIndex && _length == _capacity) {
|
||||
_firstIndex = (_firstIndex < _capacity - 1 ? _firstIndex + 1 : 0);
|
||||
}
|
||||
}
|
||||
|
||||
return _length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element from the RingBuffer and returns it.
|
||||
* If the RingBuffer is empty, shift() will return undefined.
|
||||
*/
|
||||
public function shift () :*
|
||||
{
|
||||
if (this.empty) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var obj :* = _array[_firstIndex];
|
||||
_array[_firstIndex] = undefined;
|
||||
_firstIndex = (_firstIndex < _capacity - 1 ? _firstIndex + 1 : 0);
|
||||
--_length;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last element from the RingBuffer and returns it.
|
||||
* If the RingBuffer is empty, pop() will return undefined.
|
||||
*/
|
||||
public function pop () :*
|
||||
{
|
||||
if (this.empty) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
var lastIndex :uint = ((_firstIndex + _length - 1) % _capacity);
|
||||
|
||||
var obj :* = _array[lastIndex];
|
||||
_array[lastIndex] = undefined;
|
||||
--_length;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/** Removes all elements from the RingBuffer. */
|
||||
public function clear () :void
|
||||
{
|
||||
_array = new Array();
|
||||
_array.length = _capacity;
|
||||
_length = 0;
|
||||
_firstIndex = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element at the specified index.
|
||||
* If index >= length, at() will return undefined.
|
||||
*/
|
||||
public function at (index :uint) :*
|
||||
{
|
||||
if (index >= _length) {
|
||||
return undefined;
|
||||
} else {
|
||||
var index :uint = ((_firstIndex + index) % _capacity);
|
||||
return _array[index];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a test function on each item in the ring buffer
|
||||
* until an item is reached that returns false for the specified
|
||||
* function.
|
||||
*
|
||||
* Returns a Boolean value of true if all items in the buffer return
|
||||
* true for the specified function; otherwise, false.
|
||||
*/
|
||||
public function every (callback :Function, thisObject :* = null) :Boolean
|
||||
{
|
||||
for (var i :int = 0; i < _length; ++i) {
|
||||
if (!callback.call(thisObject, this.at(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a function on each item in the ring buffer.
|
||||
*/
|
||||
public function forEach (callback :Function, thisObject :* = null) :void
|
||||
{
|
||||
for (var i :int = 0; i < _length; ++i) {
|
||||
callback.call(thisObject, this.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for an item in the ring buffer by using strict equality
|
||||
* (===) and returns the index position of the item, or -1
|
||||
* if the item is not found.
|
||||
*/
|
||||
public function indexOf (searchElement :*, fromIndex :int = 0) :int
|
||||
{
|
||||
for (var i :int = 0; i < _length; ++i) {
|
||||
if (this.at(i) === searchElement) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected var _array :Array = new Array();
|
||||
protected var _capacity :uint;
|
||||
protected var _length :uint;
|
||||
protected var _firstIndex :uint;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public interface Set
|
||||
{
|
||||
/**
|
||||
* Adds the specified element to the set if it's not already present.
|
||||
* Returns true if the set did not already contain the specified element.
|
||||
*/
|
||||
function add (o :Object) :Boolean;
|
||||
|
||||
/**
|
||||
* Removes the specified element from this set if it is present.
|
||||
* Returns true if the set contained the specified element.
|
||||
*/
|
||||
function remove (o :Object) :Boolean;
|
||||
|
||||
/** Remove all elements from this set. */
|
||||
function clear () :void;
|
||||
|
||||
/** Returns true if this set contains the specified element. */
|
||||
function contains (o :Object) :Boolean;
|
||||
|
||||
/** Call the specified function, which accepts an element as an argument. */
|
||||
function forEach (fn :Function) :void;
|
||||
|
||||
/** Retuns the number of elements in this set. */
|
||||
function size () :int; // @TSC - should this be uint?
|
||||
|
||||
/** Returns true if this set contains no elements. */
|
||||
function isEmpty () :Boolean;
|
||||
|
||||
/**
|
||||
* Returns all elements in the set in an Array.
|
||||
*
|
||||
* <p>Note: this interface does not specify whether modifying the returned Array will modify
|
||||
* the underlying set; the decision is up to implementation.
|
||||
*/
|
||||
function toArray () :Array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Guarantees a specific iteration order for keys(), values(), and forEach() with as little
|
||||
* additional overhead as possible.
|
||||
*/
|
||||
public class SortedHashMap extends HashMap
|
||||
{
|
||||
public static const COMPARABLE_KEYS :int = 0;
|
||||
public static const STRING_KEYS :int = 1;
|
||||
public static const NUMERIC_KEYS :int = 2;
|
||||
|
||||
public function SortedHashMap (
|
||||
keyType :int,
|
||||
loadFactor :Number = 1.75, equalsFn :Function = null, hashFn :Function = null)
|
||||
{
|
||||
super(loadFactor, equalsFn, hashFn);
|
||||
_keyType = keyType;
|
||||
}
|
||||
|
||||
override public function put (key :Object, value :Object) :*
|
||||
{
|
||||
validateKey(key);
|
||||
return super.put(key, value);
|
||||
}
|
||||
|
||||
override public function keys () :Array
|
||||
{
|
||||
var keys :Array = super.keys();
|
||||
|
||||
switch (_keyType) {
|
||||
case COMPARABLE_KEYS:
|
||||
ArrayUtil.sort(keys);
|
||||
break;
|
||||
|
||||
case STRING_KEYS:
|
||||
default:
|
||||
keys.sort();
|
||||
break;
|
||||
|
||||
case NUMERIC_KEYS:
|
||||
keys.sort(Array.NUMERIC);
|
||||
break;
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
override public function values () :Array
|
||||
{
|
||||
// not very optimal, we need to look up each value from the sorted keys.
|
||||
return keys().map(Util.adapt(get));
|
||||
}
|
||||
|
||||
override public function forEach (fn :Function) :void
|
||||
{
|
||||
var keys :Array = keys();
|
||||
for each (var key :Object in keys) {
|
||||
fn(key, get(key));
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateKey (key :Object) :void
|
||||
// throws ArgumentError
|
||||
{
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
switch (_keyType) {
|
||||
case COMPARABLE_KEYS:
|
||||
if (key is Comparable) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case STRING_KEYS:
|
||||
if (key is String) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case NUMERIC_KEYS:
|
||||
if (key is Number) {
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// there is no return
|
||||
break;
|
||||
}
|
||||
|
||||
throw new ArgumentError("Invalid key");
|
||||
}
|
||||
|
||||
/** The key type in use for this map. */
|
||||
protected var _keyType :int;
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
public class StringBuilder
|
||||
{
|
||||
public function StringBuilder (... args)
|
||||
{
|
||||
append.apply(this, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Append all arguments to the end of the string being built
|
||||
* and return this StringBuilder.
|
||||
*/
|
||||
public function append (... args) :StringBuilder
|
||||
{
|
||||
_buf += args.join("");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the String built so far.
|
||||
*/
|
||||
public function toString () :String
|
||||
{
|
||||
// it's ok, Strings are immutable
|
||||
return _buf;
|
||||
}
|
||||
|
||||
/** The string upon which we build. Internally in AVM2, Strings have
|
||||
* been designed with a prefix pointer so that concatination is
|
||||
* really cheap. */
|
||||
protected var _buf :String = "";
|
||||
}
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.getTimer; // function import
|
||||
|
||||
/**
|
||||
* A throttle is used to prevent code from attempting a particular operation too often. Often it is
|
||||
* desirable to retry an operation under failure conditions, but simplistic approaches to retrying
|
||||
* operations can lead to large numbers of spurious attempts to do something that will obviously
|
||||
* fail. The throttle class provides a mechanism for limiting such attempts by measuring whether or
|
||||
* not an activity has been performed N times in the last M seconds. The user of the class decides
|
||||
* the appropriate throttle parameters and then simply calls through to throttle to determine
|
||||
* whether or not to go ahead with the operation.
|
||||
*
|
||||
* <p> For example:
|
||||
*
|
||||
* <pre>
|
||||
* protected Throttle _throttle = new Throttle(5, 60000L);
|
||||
*
|
||||
* public void performOp ()
|
||||
* throws UnavailableException
|
||||
* {
|
||||
* if (_throttle.throttleOp()) {
|
||||
* throw new UnavailableException();
|
||||
* }
|
||||
*
|
||||
* // perform operation
|
||||
* }
|
||||
* </pre>
|
||||
*/
|
||||
public class Throttle
|
||||
{
|
||||
/**
|
||||
* Constructs a new throttle instance that will allow the specified number of operations to
|
||||
* proceed within the specified period (the period is measured in milliseconds).
|
||||
*
|
||||
* <p> As operations and period define a ratio, use the smallest value possible for
|
||||
* <code>operations</code> as an array is created to track the time at which each operation was
|
||||
* performed (e.g. use 6 ops per 10 seconds rather than 60 ops per 100 seconds if
|
||||
* possible). However, note that you may not always want to reduce the ratio as much as
|
||||
* possible if you wish to allow bursts of operations up to some large value.
|
||||
*/
|
||||
public function Throttle (operations :int, period :int)
|
||||
{
|
||||
_ops = ArrayUtil.create(operations, 0);
|
||||
_period = period;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the number of operations for this throttle to a new maximum, retaining the current
|
||||
* history of operations if the limit is being increased and truncating the newest operations
|
||||
* if the limit is decreased.
|
||||
*
|
||||
* @param operations the new maximum number of operations.
|
||||
* @param period the new period (in milliseconds).
|
||||
*/
|
||||
public function reinit (operations :int, period :int) :void
|
||||
{
|
||||
_period = period;
|
||||
if (operations != _ops.length) {
|
||||
var ops :Array = ArrayUtil.create(operations, 0);
|
||||
|
||||
if (operations > _ops.length) {
|
||||
// copy to a larger buffer, leaving zeroes at the beginning
|
||||
var oldestOp :int = _oldestOp + operations - _ops.length;
|
||||
ArrayUtil.copy(_ops, 0, ops, 0, _oldestOp);
|
||||
ArrayUtil.copy(_ops, _oldestOp, ops, oldestOp, _ops.length - _oldestOp);
|
||||
|
||||
} else {
|
||||
// if we're truncating, copy the first (oldest) stamps into ops[0..]
|
||||
var endCount :int = Math.min(_ops.length - _oldestOp, operations);
|
||||
ArrayUtil.copy(_ops, _oldestOp, ops, 0, endCount);
|
||||
ArrayUtil.copy(_ops, 0, ops, endCount, operations - endCount);
|
||||
_oldestOp = 0;
|
||||
}
|
||||
_ops = ops;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attempt at an operation and returns false if the operation should be performed
|
||||
* or true if it should be throttled (meaning N operations have already been performed in the
|
||||
* last M seconds).
|
||||
*
|
||||
* @return true if the throttle is activated, false if the operation can proceed.
|
||||
*/
|
||||
public function throttleOp () :Boolean
|
||||
{
|
||||
return throttleOpAt(getTimer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an attempt at an operation and returns false if the operation should be performed
|
||||
* or true if it should be throttled (meaning N operations have already been performed in the
|
||||
* last M seconds).
|
||||
*
|
||||
* @param timeStamp the timestamp at which this operation is being attempted.
|
||||
*
|
||||
* @return true if the throttle is activated, false if the operation can proceed.
|
||||
*/
|
||||
public function throttleOpAt (timeStamp :int) :Boolean
|
||||
{
|
||||
if (wouldThrottle(timeStamp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
noteOp(timeStamp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if we would throttle an operation occuring at the specified timestamp.
|
||||
* Typically used in conjunction with {@link #noteOp}.
|
||||
*/
|
||||
public function wouldThrottle (timeStamp :int) :Boolean
|
||||
{
|
||||
// if the oldest operation was performed less than _period ago, we need to throttle
|
||||
var elapsed :int = timeStamp - _ops[_oldestOp];
|
||||
// cope with negative time elapsed (shouldn't happen) by not throttling
|
||||
return (elapsed >= 0 && elapsed < _period);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that an operation occurred at the specified timestamp. This method should be used with
|
||||
* {@link #wouldThrottle} to note an operation that has already been cleared to
|
||||
* occur. Typically this is used if there is another limiting factor besides the throttle that
|
||||
* determines whether the operation can occur. You are responsible for calling this method in a
|
||||
* safe and timely manner after using wouldThrottle.
|
||||
*/
|
||||
public function noteOp (timeStamp :int) :void
|
||||
{
|
||||
// overwrite the oldest operation with the current time and move the oldest operation
|
||||
// pointer to the second oldest operation (which is now the oldest as we overwrote the
|
||||
// oldest)
|
||||
_ops[_oldestOp] = timeStamp;
|
||||
_oldestOp = (_oldestOp + 1) % _ops.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the most recently recorded operation.
|
||||
*/
|
||||
public function getLatestOperation () :int
|
||||
{
|
||||
return _ops[(_oldestOp + _ops.length - 1) % _ops.length];
|
||||
}
|
||||
|
||||
// from Object
|
||||
public function toString () :String
|
||||
{
|
||||
var oldest :int = getTimer() - _ops[_oldestOp];
|
||||
return _ops.length + " ops per " + _period + "ms (oldest " + oldest + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* For debugging, includes the age of all operations.
|
||||
*/
|
||||
public function opsToString () :String
|
||||
{
|
||||
var now :int = getTimer();
|
||||
var ages :Array = []
|
||||
for (var ii :int = 0; ii < _ops.length; ++ii) {
|
||||
ages.push(now - _ops[ii]);
|
||||
}
|
||||
return "[" + StringUtil.toString(ages) + "] [Oldest idx = " + _oldestOp + "]";
|
||||
}
|
||||
|
||||
protected var _ops :Array;
|
||||
protected var _oldestOp :int;
|
||||
protected var _period :int;
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* Utility for times.
|
||||
*/
|
||||
public class TimeUtil
|
||||
{
|
||||
/** Time unit constant. */
|
||||
public static const MILLISECOND :int = 0;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const SECOND :int = 1;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const MINUTE :int = 2;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const HOUR :int = 3;
|
||||
|
||||
/** Time unit constant. */
|
||||
public static const DAY :int = 4;
|
||||
|
||||
// TODO: Weeks?, months?
|
||||
protected static const MAX_UNIT :int = DAY;
|
||||
|
||||
/**
|
||||
* Get a translatable string specifying the magnitude of the specified
|
||||
* duration. Results will be between "1 second" and "X hours", with
|
||||
* all times rounded to the nearest unit. "0 units" will never be
|
||||
* displayed, the minimum is 1.
|
||||
*/
|
||||
public static function getTimeOrderString (
|
||||
duration :Number, minUnit :int, maxUnit :int = -1) :String
|
||||
{
|
||||
// enforce sanity
|
||||
if (maxUnit == -1) {
|
||||
maxUnit = MAX_UNIT;
|
||||
}
|
||||
minUnit = Math.min(minUnit, maxUnit);
|
||||
maxUnit = Math.min(maxUnit, MAX_UNIT);
|
||||
|
||||
for (var uu :int = MILLISECOND; uu <= MAX_UNIT; uu++) {
|
||||
var quantity :int = getQuantityPerUnit(uu);
|
||||
if ((minUnit <= uu) && (duration < quantity || maxUnit == uu)) {
|
||||
duration = Math.max(1, duration);
|
||||
return MessageBundle.tcompose(getTransKey(uu),
|
||||
String(duration));
|
||||
}
|
||||
duration = Math.round(duration / quantity);
|
||||
}
|
||||
|
||||
// will not happen, because eventually uu will be MAX_UNIT
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translatable string specifying the duration, down to the
|
||||
* minimum granularity.
|
||||
*/
|
||||
public static function getTimeString (
|
||||
duration :Number, minUnit :int) :String
|
||||
{
|
||||
// sanity
|
||||
minUnit = Math.min(minUnit, MAX_UNIT);
|
||||
duration = Math.abs(duration);
|
||||
|
||||
var list :Array = new Array();
|
||||
var parts :int = 0; // how many parts are in the translation string?
|
||||
for (var uu :int = MILLISECOND; uu <= MAX_UNIT; uu++) {
|
||||
var quantity :int = getQuantityPerUnit(uu);
|
||||
if (minUnit <= uu) {
|
||||
list.push(MessageBundle.tcompose(getTransKey(uu),
|
||||
String(duration % quantity)));
|
||||
parts++;
|
||||
}
|
||||
duration /= quantity;
|
||||
if (duration <= 0 && parts > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (parts == 1) {
|
||||
return (list[0] as String);
|
||||
} else {
|
||||
return MessageBundle.compose("m.times_" + parts, list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to get the quantity for the specified unit.
|
||||
* (Not very OO)
|
||||
*/
|
||||
protected static function getQuantityPerUnit (unit :int) :int
|
||||
{
|
||||
switch (unit) {
|
||||
case MILLISECOND: return 1000;
|
||||
case SECOND: case MINUTE: return 60;
|
||||
case HOUR: return 24;
|
||||
case DAY: return int.MAX_VALUE;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to get the translation key for the specified unit.
|
||||
* (Not very OO)
|
||||
*/
|
||||
protected static function getTransKey (unit :int) :String
|
||||
{
|
||||
switch (unit) {
|
||||
case MILLISECOND: return "m.millisecond";
|
||||
case SECOND: return "m.second";
|
||||
case MINUTE: return "m.minute";
|
||||
case HOUR: return "m.hour";
|
||||
case DAY: return "m.day";
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.ByteArray;
|
||||
import flash.utils.getQualifiedClassName;
|
||||
|
||||
public class Util
|
||||
{
|
||||
/**
|
||||
* Initialize the target object with values present in the initProps object and the defaults
|
||||
* object. Neither initProps nor defaults will be modified.
|
||||
* @throws ReferenceError if a property cannot be set on the target object.
|
||||
*
|
||||
* @param target any object or class instance.
|
||||
* @param initProps a plain Object hash containing names and properties to set on the target
|
||||
* object.
|
||||
* @param defaults a plain Object hash containing names and properties to set on the target
|
||||
* object, only if the same property name does not exist in initProps.
|
||||
* @param maskProps a plain Object hash containing names of properties to omit setting
|
||||
* from the initProps object. This allows you to add custom properties to
|
||||
* initProps without having to modify the value from your callers.
|
||||
*/
|
||||
public static function init (
|
||||
target :Object, initProps :Object, defaults :Object = null, maskProps :Object = null) :void
|
||||
{
|
||||
var prop :String;
|
||||
for (prop in initProps) {
|
||||
if (maskProps == null || !(prop in maskProps)) {
|
||||
target[prop] = initProps[prop];
|
||||
}
|
||||
}
|
||||
|
||||
if (defaults != null) {
|
||||
for (prop in defaults) {
|
||||
if (initProps == null || !(prop in initProps)) {
|
||||
target[prop] = defaults[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a var-args function that will attempt to pass only the arguments accepted by the
|
||||
* passed-in function. Does not work if the passed-in function is varargs, and anyway
|
||||
* then you don't need adapting, do you?
|
||||
*/
|
||||
public static function adapt (fn :Function) :Function
|
||||
{
|
||||
return function (... args) :* {
|
||||
args.length = fn.length; // fit the args to the fn, filling in 'undefined' if growing
|
||||
return fn.apply(null, args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the specified object is just a regular old associative hash.
|
||||
*/
|
||||
public static function isPlainObject (obj :Object) :Boolean
|
||||
{
|
||||
return getQualifiedClassName(obj) == "Object";
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the specified object 'simple': one of the basic built-in flash types.
|
||||
*/
|
||||
public static function isSimple (obj :Object) :Boolean
|
||||
{
|
||||
var type :String = typeof(obj);
|
||||
switch (type) {
|
||||
case "number":
|
||||
case "string":
|
||||
case "boolean":
|
||||
return true;
|
||||
|
||||
case "object":
|
||||
return (obj is Date) || (obj is Array);
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the property keys of the specified object, in their
|
||||
* natural iteration order.
|
||||
*/
|
||||
public static function keys (obj :Object) :Array
|
||||
{
|
||||
var arr :Array = [];
|
||||
for (var k :* in obj) { // no "each": iterate over keys
|
||||
arr.push(k);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array containing the property values of the specified object, in their
|
||||
* natural iteration order.
|
||||
*/
|
||||
public static function values (obj :Object) :Array
|
||||
{
|
||||
var arr :Array = [];
|
||||
for each (var v :* in obj) { // "each" iterates over values
|
||||
arr.push(v);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the 'value' object into XML safely. This is equivalent to <code>new XML(value)</code>
|
||||
* but offers protection from other code that may have changing the default settings
|
||||
* used for parsing XML. Also, if you would like to use non-standard parsing settings
|
||||
* this method will protect other code from being broken by you.
|
||||
*
|
||||
* @param value the value to parse into XML.
|
||||
* @param settings an Object containing your desired XML settings, or null (or omitted) to
|
||||
* use the default settings.
|
||||
* @see XML#setSettings()
|
||||
*/
|
||||
public static function newXML (value :Object, settings :Object = null) :XML
|
||||
{
|
||||
return safeXMLOp(function () :* {
|
||||
return new XML(value);
|
||||
}, settings) as XML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call toString() on the specified XML object safely. This is equivalent to
|
||||
* <code>xml.toString()</code> but offers protection from other code that may have changed
|
||||
* the default settings used for stringing XML. Also, if you would like to use the
|
||||
* non-standard printing settings this method will protect other code from being
|
||||
* broken by you.
|
||||
*
|
||||
* @param xml the xml value to Stringify.
|
||||
* @param settings an Object containing your desired XML settings, or null (or omitted) to
|
||||
* use the default settings.
|
||||
* @see XML#toString()
|
||||
* @see XML#setSettings()
|
||||
*/
|
||||
public static function XMLtoString (xml :XML, settings :Object = null) :String
|
||||
{
|
||||
return safeXMLOp(function () :* {
|
||||
return xml.toString();
|
||||
}, settings) as String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call toXMLString() on the specified XML object safely. This is equivalent to
|
||||
* <code>xml.toXMLString()</code> but offers protection from other code that may have changed
|
||||
* the default settings used for stringing XML. Also, if you would like to use the
|
||||
* non-standard printing settings this method will protect other code from being
|
||||
* broken by you.
|
||||
*
|
||||
* @param xml the xml value to Stringify.
|
||||
* @param settings an Object containing your desired XML settings, or null (or omitted) to
|
||||
* use the default settings.
|
||||
* @see XML#toXMLString()
|
||||
* @see XML#setSettings()
|
||||
*/
|
||||
public static function XMLtoXMLString (xml :XML, settings :Object = null) :String
|
||||
{
|
||||
return safeXMLOp(function () :* {
|
||||
return xml.toXMLString();
|
||||
}, settings) as String;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an operation on XML that takes place using the specified settings, and
|
||||
* restores the XML settings to their previous values.
|
||||
*
|
||||
* @param fn a function to be called with no arguments.
|
||||
* @param settings an Object containing your desired XML settings, or null (or omitted) to
|
||||
* use the default settings.
|
||||
*
|
||||
* @return the return value of your function, if any.
|
||||
* @see XML#setSettings()
|
||||
* @see XML#settings()
|
||||
*/
|
||||
public static function safeXMLOp (fn :Function, settings :Object = null) :*
|
||||
{
|
||||
var oldSettings :Object = XML.settings();
|
||||
try {
|
||||
XML.setSettings(settings); // setting to null resets to all the defaults
|
||||
return fn();
|
||||
} finally {
|
||||
XML.setSettings(oldSettings);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A nice utility method for testing equality in a better way.
|
||||
* If the objects are Equalable, then that will be tested. Arrays
|
||||
* and ByteArrays are also compared and are equal if they have
|
||||
* elements that are equals (deeply).
|
||||
*/
|
||||
public static function equals (obj1 :Object, obj2 :Object) :Boolean
|
||||
{
|
||||
// catch various common cases (both primitive or null)
|
||||
if (obj1 === obj2) {
|
||||
return true;
|
||||
|
||||
} else if (obj1 is Equalable) {
|
||||
// if obj1 is Equalable, then that decides it
|
||||
return (obj1 as Equalable).equals(obj2);
|
||||
|
||||
} else if ((obj1 is Array) && (obj2 is Array)) {
|
||||
return ArrayUtil.equals(obj1 as Array, obj2 as Array);
|
||||
|
||||
} else if ((obj1 is ByteArray) && (obj2 is ByteArray)) {
|
||||
var ba1 :ByteArray = (obj1 as ByteArray);
|
||||
var ba2 :ByteArray = (obj2 as ByteArray);
|
||||
if (ba1.length != ba2.length) {
|
||||
return false;
|
||||
}
|
||||
for (var ii :int = 0; ii < ba1.length; ii++) {
|
||||
if (ba1[ii] != ba2[ii]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you call a varargs method by passing it an array, the array
|
||||
* will end up being arg 1.
|
||||
*/
|
||||
public static function unfuckVarargs (args :Array) :Array
|
||||
{
|
||||
return (args.length == 1 && (args[0] is Array)) ? (args[0] as Array)
|
||||
: args;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.events.Event;
|
||||
|
||||
/**
|
||||
* A handy event for simply dispatching a value associated with the event type.
|
||||
*/
|
||||
public class ValueEvent extends Event
|
||||
{
|
||||
/**
|
||||
* Accessor: get the value.
|
||||
*/
|
||||
public function get value () :Object
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the value event.
|
||||
*/
|
||||
public function ValueEvent (
|
||||
type :String, value :Object, bubbles :Boolean = false, cancelable :Boolean = false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
_value = value;
|
||||
}
|
||||
|
||||
override public function clone () :Event
|
||||
{
|
||||
return new ValueEvent(type, _value, bubbles, cancelable);
|
||||
}
|
||||
|
||||
/** The value. */
|
||||
protected var _value :Object;
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
import flash.utils.Dictionary;
|
||||
|
||||
/**
|
||||
* A weak reference. At some point in the future the referent might not be available
|
||||
* anymore.
|
||||
*/
|
||||
public class WeakReference
|
||||
{
|
||||
/**
|
||||
* No, you cannot store undefined here.
|
||||
*/
|
||||
public function WeakReference (referant :Object)
|
||||
{
|
||||
_ref[referant] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the referant, or undefined if it's been collected.
|
||||
*/
|
||||
public function get () :*
|
||||
{
|
||||
for (var k :* in _ref) {
|
||||
return k;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** The only way to have a weak reference in actionscript. */
|
||||
protected var _ref :Dictionary = new Dictionary(true);
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2009 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://www.threerings.net/code/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.util {
|
||||
|
||||
/**
|
||||
* A HashMap that stores values weakly: if not referenced anywhere else, they may
|
||||
* be garbage collected. Note that the size might change unexpectedly as things are removed.
|
||||
*/
|
||||
public class WeakValueHashMap extends HashMap
|
||||
{
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function WeakValueHashMap (
|
||||
loadFactor :Number = 1.75,
|
||||
equalsFn :Function = null,
|
||||
hashFn :Function = null)
|
||||
{
|
||||
super(loadFactor, equalsFn, hashFn);
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
override public function get (key :Object) :*
|
||||
{
|
||||
var result :* = super.get(key);
|
||||
if (result is WeakReference) { // could also just be undefined or null
|
||||
result = WeakReference(result).get();
|
||||
if (result === undefined) {
|
||||
super.remove(key);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
override public function put (key :Object, value :Object) :*
|
||||
{
|
||||
// store nulls directly
|
||||
return unwrap(super.put(key, (value == null) ? null : new WeakReference(value)));
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
override public function remove (key :Object) :*
|
||||
{
|
||||
return unwrap(super.remove(key));
|
||||
}
|
||||
|
||||
/** @inheritDoc */
|
||||
override public function size () :int
|
||||
{
|
||||
forEach0(function (k :*, v: *) :void {});
|
||||
return super.size(); // alternately, we could prune and increment a count for all present..
|
||||
}
|
||||
|
||||
/** @private */
|
||||
override protected function forEach0 (fn :Function) :void
|
||||
{
|
||||
var removeKeys :Array = [];
|
||||
super.forEach0(function (key :*, value :WeakReference) :void {
|
||||
var rawVal :* = (value == null) ? null : value.get();
|
||||
if (rawVal === undefined) {
|
||||
removeKeys.push(key);
|
||||
} else {
|
||||
fn(key, rawVal);
|
||||
}
|
||||
});
|
||||
for each (var key :Object in removeKeys) {
|
||||
super.remove(key); // slightly more efficient, since we don't need to unwrap
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwrap a possible WeakReference for returning to the user.
|
||||
* @private
|
||||
*/
|
||||
protected function unwrap (val :*) :*
|
||||
{
|
||||
return (val is WeakReference) ? WeakReference(val).get() : val;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user