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:
Michael Bayne
2009-07-21 20:56:35 +00:00
parent f005c3e3a8
commit 805ef99374
60 changed files with 26 additions and 6824 deletions
+1
View File
@@ -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>
+1
View File
@@ -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>
+3 -2
View File
@@ -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;
}
}
-355
View File
@@ -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);
}
}
}
}
-98
View File
@@ -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;
}
}
-16
View File
@@ -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);
}
}
}
-130
View File
@@ -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);
}
}
}
-31
View File
@@ -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;
}
}
-93
View File
@@ -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;
}
}
-156
View File
@@ -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;
}
}
-33
View File
@@ -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;
}
}
-56
View File
@@ -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
}
}
}
}
-161
View File
@@ -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);
}
}
}
-107
View File
@@ -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;
}
}
-86
View File
@@ -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" ];
}
}
-191
View File
@@ -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
}
}
-36
View File
@@ -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;
}
}
-223
View File
@@ -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);
}
}
-38
View File
@@ -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() : "");
}
}
}
-352
View File
@@ -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);
}
}
}
-104
View File
@@ -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;
}
}
-32
View File
@@ -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;
}
}
-47
View File
@@ -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;
}
}
-138
View File
@@ -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;
}
}
-135
View File
@@ -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;
}
}
}
-345
View File
@@ -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 ];
}
}
-34
View File
@@ -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;
}
}
-46
View File
@@ -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");
}
}
-88
View File
@@ -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;
}
}
-437
View File
@@ -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";
}
}
-81
View File
@@ -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);
}
}
-344
View File
@@ -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();
}
}
-60
View File
@@ -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!
}
}
}
-131
View File
@@ -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;
}
}
-83
View File
@@ -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();
}
}
-109
View File
@@ -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();
}
}
-132
View File
@@ -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);
}
}
}
-102
View File
@@ -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;
};
}
}
}
-148
View File
@@ -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;
}
}
-74
View File
@@ -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)];
}
}
}
}
-226
View File
@@ -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;
}
}
-62
View File
@@ -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;
}
}
-120
View File
@@ -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 = "";
}
}
-191
View File
@@ -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;
}
}
-141
View File
@@ -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;
}
}
}
}
-256
View File
@@ -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;
}
}
}
-57
View File
@@ -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;
}
}
}