Squashed 'cloner/' content from commit 79ff709
git-subtree-dir: cloner git-subtree-split: 79ff709d0060b967d3508b6abfe194bedfb63f1b
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1 +1 @@
|
||||
bin/
|
||||
bin/
|
||||
|
@@ -36,4 +36,4 @@ install:
|
||||
curl -sSLf https://lib.haxe.org/p/jQueryExtern/3.2.1/download/ -o /dev/null;
|
||||
fi
|
||||
|
||||
script: ./test.sh
|
||||
script: ./test.sh
|
||||
|
@@ -71,4 +71,4 @@ At the next milestone:
|
||||
|
||||
Hopefully someday:
|
||||
|
||||
* Professional game developers who want to learn the ways of Lisp
|
||||
* Professional game developers who want to learn the ways of Lisp
|
||||
|
1
cloner/.gitignore
vendored
Normal file
1
cloner/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
bin/test/
|
13
cloner/.travis.yml
Normal file
13
cloner/.travis.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
language: objective-c
|
||||
|
||||
install:
|
||||
- brew install neko haxe
|
||||
- cd ..
|
||||
- mkdir haxelib
|
||||
- haxelib setup haxelib
|
||||
- haxelib install hxcpp > /dev/null
|
||||
- haxelib install hx3compat > /dev/null
|
||||
before_script:
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
script:
|
||||
- sh test.sh -e
|
24
cloner/LICENSE.md
Normal file
24
cloner/LICENSE.md
Normal file
@@ -0,0 +1,24 @@
|
||||
License
|
||||
=======
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Thomas Uster
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
12
cloner/README.md
Normal file
12
cloner/README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/thomasuster/cloner)
|
||||
[](https://ci.appveyor.com/project/thomasuster/cloner)
|
||||
|
||||
```
|
||||
git clone https://github.com/thomasuster/cloner.git
|
||||
haxelib dev cloner cloner/src
|
||||
```
|
||||
```
|
||||
var cloner:Cloner = new Cloner();
|
||||
var clone = cloner.clone(mostAnything);
|
||||
```
|
33
cloner/appveyor.yml
Normal file
33
cloner/appveyor.yml
Normal file
@@ -0,0 +1,33 @@
|
||||
version: "{build}"
|
||||
|
||||
clone_folder: C:\projects\cloner
|
||||
|
||||
environment:
|
||||
global:
|
||||
HAXELIB_ROOT: C:\projects\haxelib
|
||||
|
||||
install:
|
||||
# Install haxe and neko
|
||||
- cinst neko --version 2.2.0 -y
|
||||
- cinst haxe -y
|
||||
- RefreshEnv
|
||||
- neko -version
|
||||
- haxe -version
|
||||
# Setup haxelib
|
||||
- mkdir "%HAXELIB_ROOT%"
|
||||
- haxelib setup "%HAXELIB_ROOT%"
|
||||
- haxelib install record-macros
|
||||
- haxelib install hxcpp
|
||||
|
||||
build_script:
|
||||
- cd test
|
||||
- haxe compile.hxml
|
||||
- cd ..
|
||||
|
||||
test_script:
|
||||
- echo CPP
|
||||
- bin\test\TestMain-debug.exe
|
||||
- echo NEKO
|
||||
- neko bin\test\TestMain.n
|
||||
|
||||
|
13
cloner/haxelib.json
Normal file
13
cloner/haxelib.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "cloner",
|
||||
"url" : "https://github.com/thomasuster/cloner",
|
||||
"license": "MIT",
|
||||
"tags": ["clone", "copy", "deep"],
|
||||
"description": "A deep copy algorithm for haxe",
|
||||
"version": "1.0.0",
|
||||
"classPath": "src/",
|
||||
"releasenote": "",
|
||||
"contributors": ["thomasuster"],
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
109
cloner/src/cloner/Cloner.hx
Normal file
109
cloner/src/cloner/Cloner.hx
Normal file
@@ -0,0 +1,109 @@
|
||||
package cloner;
|
||||
import Array;
|
||||
import haxe.ds.ObjectMap;
|
||||
import Type.ValueType;
|
||||
import haxe.ds.IntMap;
|
||||
import haxe.ds.StringMap;
|
||||
class Cloner {
|
||||
|
||||
var cache:ObjectMap<Dynamic,Dynamic>;
|
||||
var classHandles:Map<String,Dynamic->Dynamic>;
|
||||
var stringMapCloner:MapCloner<String>;
|
||||
var intMapCloner:MapCloner<Int>;
|
||||
|
||||
public function new():Void {
|
||||
stringMapCloner = new MapCloner(this,StringMap);
|
||||
intMapCloner = new MapCloner(this,IntMap);
|
||||
classHandles = new Map<String,Dynamic->Dynamic>();
|
||||
classHandles.set('String',returnString);
|
||||
classHandles.set('Array',cloneArray);
|
||||
classHandles.set('haxe.ds.StringMap',stringMapCloner.clone);
|
||||
classHandles.set('haxe.ds.IntMap',intMapCloner.clone);
|
||||
}
|
||||
|
||||
function returnString(v:String):String {
|
||||
return v;
|
||||
}
|
||||
|
||||
public function clone <T> (v:T):T {
|
||||
cache = new ObjectMap<Dynamic,Dynamic>();
|
||||
var outcome:T = _clone(v);
|
||||
cache = null;
|
||||
return outcome;
|
||||
}
|
||||
|
||||
public function _clone <T> (v:T):T {
|
||||
#if js
|
||||
if(Std.is(v, String))
|
||||
return v;
|
||||
#end
|
||||
|
||||
#if neko
|
||||
try {
|
||||
if(Type.getClassName(cast v) != null)
|
||||
return v;
|
||||
}catch(e:Dynamic) {}
|
||||
#else
|
||||
if(Type.getClassName(cast v) != null)
|
||||
return v;
|
||||
#end
|
||||
switch(Type.typeof(v)){
|
||||
case TNull:
|
||||
return null;
|
||||
case TInt:
|
||||
return v;
|
||||
case TFloat:
|
||||
return v;
|
||||
case TBool:
|
||||
return v;
|
||||
case TObject:
|
||||
return handleAnonymous(v);
|
||||
case TFunction:
|
||||
return null;
|
||||
case TClass(c):
|
||||
if(!cache.exists(v))
|
||||
cache.set(v,handleClass(c, v));
|
||||
return cache.get(v);
|
||||
case TEnum(e):
|
||||
return v;
|
||||
case TUnknown:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleAnonymous (v:Dynamic):Dynamic {
|
||||
var properties:Array<String> = Reflect.fields(v);
|
||||
var anonymous:Dynamic = {};
|
||||
for (i in 0...properties.length) {
|
||||
var property:String = properties[i];
|
||||
Reflect.setField(anonymous, property, _clone(Reflect.getProperty(v, property)));
|
||||
}
|
||||
return anonymous;
|
||||
}
|
||||
|
||||
function handleClass <T> (c:Class<T>,inValue:T):T {
|
||||
var handle:T->T = classHandles.get(Type.getClassName(c));
|
||||
if(handle == null)
|
||||
handle = cloneClass;
|
||||
return handle(inValue);
|
||||
}
|
||||
|
||||
function cloneArray <T> (inValue:Array<T>):Array<T> {
|
||||
var array:Array<T> = inValue.copy();
|
||||
for (i in 0...array.length)
|
||||
array[i] = _clone(array[i]);
|
||||
return array;
|
||||
}
|
||||
|
||||
function cloneClass <T> (inValue:T):T {
|
||||
var outValue:T = Type.createEmptyInstance(Type.getClass(inValue));
|
||||
var fields:Array<String> = Reflect.fields(inValue);
|
||||
for (i in 0...fields.length) {
|
||||
var field = fields[i];
|
||||
var property = Reflect.getProperty(inValue, field);
|
||||
Reflect.setField(outValue, field, _clone(property));
|
||||
}
|
||||
return outValue;
|
||||
}
|
||||
|
||||
}
|
25
cloner/src/cloner/MapCloner.hx
Normal file
25
cloner/src/cloner/MapCloner.hx
Normal file
@@ -0,0 +1,25 @@
|
||||
package cloner;
|
||||
import haxe.Constraints.IMap;
|
||||
import cloner.Cloner;
|
||||
import Type.ValueType;
|
||||
class MapCloner<K>{
|
||||
|
||||
var cloner:Cloner;
|
||||
var type:Class<IMap<K,Dynamic>>;
|
||||
var noArgs:Array<Dynamic>;
|
||||
|
||||
public function new(cloner:Cloner, type:Class<IMap<K,Dynamic>>):Void {
|
||||
this.cloner = cloner;
|
||||
this.type = type;
|
||||
noArgs = [];
|
||||
}
|
||||
|
||||
public function clone <K,Dynamic> (inValue:IMap<K,Dynamic>):IMap<K,Dynamic> {
|
||||
var inMap:IMap<K,Dynamic> = inValue;
|
||||
var map:IMap<K,Dynamic> = cast Type.createInstance(type, noArgs);
|
||||
for (key in inMap.keys()) {
|
||||
map.set(key, cloner._clone(inMap.get(key)));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
8
cloner/test.sh
Normal file
8
cloner/test.sh
Normal file
@@ -0,0 +1,8 @@
|
||||
set -e
|
||||
cd test
|
||||
haxe compile.hxml
|
||||
cd ..
|
||||
echo CPP
|
||||
bin/test/TestMain-debug
|
||||
echo NEKO
|
||||
neko bin/test/TestMain.n
|
94
cloner/test/TestCloner.hx
Normal file
94
cloner/test/TestCloner.hx
Normal file
@@ -0,0 +1,94 @@
|
||||
import classes.ClassTypeProperty;
|
||||
import classes.ABCEnum;
|
||||
import cloner.Cloner;
|
||||
import classes.ArrayProperty;
|
||||
import classes.ClassProperty;
|
||||
import classes.BoolProperty;
|
||||
import classes.NullClass;
|
||||
class TestCloner extends haxe.unit.TestCase {
|
||||
|
||||
var cloner:Cloner;
|
||||
|
||||
override public function setup() {
|
||||
cloner = new Cloner();
|
||||
}
|
||||
|
||||
public function testclone() {
|
||||
var value:NullClass = new NullClass();
|
||||
assertTrue(cloner.clone(value) != null);
|
||||
}
|
||||
|
||||
public function testBoolProperty() {
|
||||
var value:BoolProperty = new BoolProperty(true);
|
||||
assertTrue(cloner.clone(value).property);
|
||||
}
|
||||
|
||||
public function testArrayProperty() {
|
||||
var value:ArrayProperty = new ArrayProperty([true]);
|
||||
assertTrue(cloner.clone(value).property[0]);
|
||||
}
|
||||
|
||||
public function testStringMap():Void {
|
||||
var value:Map<String, Int> = new Map<String, Int>();
|
||||
value.set('you',1);
|
||||
assertTrue(cloner.clone(value).get('you') == 1);
|
||||
}
|
||||
|
||||
public function testIntMap():Void {
|
||||
var value:Map<Int, Int> = new Map<Int, Int>();
|
||||
value.set(1,2);
|
||||
assertTrue(cloner.clone(value).get(1) == 2);
|
||||
}
|
||||
|
||||
public function testObjectsInMap():Void {
|
||||
var value:Map<Int, NullClass> = new Map<Int, NullClass>();
|
||||
var v:NullClass = new NullClass();
|
||||
value.set(1,v);
|
||||
assertTrue(cloner.clone(value).get(1) != v);
|
||||
}
|
||||
|
||||
public function testString():Void {
|
||||
assertTrue(cloner.clone('a') == 'a');
|
||||
}
|
||||
|
||||
public function testClassType():Void {
|
||||
assertTrue(cloner.clone(NullClass) == NullClass);
|
||||
}
|
||||
|
||||
public function testEnum():Void {
|
||||
assertTrue(cloner.clone(ABCEnum.b) == ABCEnum.b);
|
||||
}
|
||||
|
||||
public function testClassProperty():Void {
|
||||
var value:ClassProperty = new ClassProperty();
|
||||
var inValue = new BoolProperty(true);
|
||||
value.property = inValue;
|
||||
var outValue:BoolProperty = cloner.clone(value).property;
|
||||
assertTrue(outValue.property);
|
||||
assertFalse(inValue == outValue);
|
||||
}
|
||||
|
||||
public function testAnonObjectAreDifferentCopies():Void {
|
||||
var value = { a: 10, b: 20 };
|
||||
var clone = cloner.clone(value);
|
||||
Reflect.setField(value, 'a', 33);
|
||||
assertEquals(10, Reflect.getProperty(clone, 'a'));
|
||||
}
|
||||
|
||||
public function testEmbededAnonObject():Void {
|
||||
var inner = {b: 5};
|
||||
var value = { a: 10, inner: inner };
|
||||
var clone = cloner.clone(value);
|
||||
Reflect.setField(inner, 'b', 33);
|
||||
|
||||
var cloneInner = Reflect.getProperty(clone, 'inner');
|
||||
assertEquals(5, Reflect.getProperty(cloneInner, 'b'));
|
||||
}
|
||||
|
||||
public function testClassRefInsideAnObj():Void {
|
||||
var value:ClassTypeProperty = new ClassTypeProperty();
|
||||
value.property = BoolProperty;
|
||||
var outValue:ClassTypeProperty = cloner.clone(value);
|
||||
assertEquals(outValue.property, BoolProperty);
|
||||
}
|
||||
}
|
35
cloner/test/TestClonerCache.hx
Normal file
35
cloner/test/TestClonerCache.hx
Normal file
@@ -0,0 +1,35 @@
|
||||
import cloner.Cloner;
|
||||
import classes.ClassProperty;
|
||||
import classes.BoolProperty;
|
||||
class TestClonerCache extends haxe.unit.TestCase {
|
||||
|
||||
var cloner:Cloner;
|
||||
var input:Array<ClassProperty>;
|
||||
var outcome:Array<ClassProperty>;
|
||||
|
||||
override public function setup() {
|
||||
cloner = new Cloner();
|
||||
}
|
||||
|
||||
public function testReferences():Void {
|
||||
setupOutcomeWithReferences();
|
||||
assertTrue(outcome[0].property == outcome[1].property);
|
||||
}
|
||||
|
||||
public function testNoSideEffects():Void {
|
||||
setupOutcomeWithReferences();
|
||||
outcome[0].property = true;
|
||||
outcome = cloner.clone(input);
|
||||
assertFalse(outcome[0].property);
|
||||
}
|
||||
|
||||
function setupOutcomeWithReferences():Void {
|
||||
var a:ClassProperty = new ClassProperty();
|
||||
var b:ClassProperty = new ClassProperty();
|
||||
var c:BoolProperty = new BoolProperty();
|
||||
a.property = c;
|
||||
b.property = c;
|
||||
input = [a, b];
|
||||
outcome = cloner.clone(input);
|
||||
}
|
||||
}
|
11
cloner/test/TestMain.hx
Normal file
11
cloner/test/TestMain.hx
Normal file
@@ -0,0 +1,11 @@
|
||||
import haxe.unit.TestRunner;
|
||||
class TestMain {
|
||||
|
||||
static function main() {
|
||||
var runner:TestRunner = new TestRunner();
|
||||
runner.add(new TestCloner());
|
||||
runner.add(new TestClonerCache());
|
||||
runner.run();
|
||||
}
|
||||
|
||||
}
|
6
cloner/test/classes/ABCEnum.hx
Normal file
6
cloner/test/classes/ABCEnum.hx
Normal file
@@ -0,0 +1,6 @@
|
||||
package classes;
|
||||
enum ABCEnum {
|
||||
a;
|
||||
b;
|
||||
c;
|
||||
}
|
7
cloner/test/classes/ArrayProperty.hx
Normal file
7
cloner/test/classes/ArrayProperty.hx
Normal file
@@ -0,0 +1,7 @@
|
||||
package classes;
|
||||
class ArrayProperty {
|
||||
public var property:Array<Bool>;
|
||||
public function new(v:Array<Bool>):Void {
|
||||
property = v;
|
||||
}
|
||||
}
|
7
cloner/test/classes/BoolProperty.hx
Normal file
7
cloner/test/classes/BoolProperty.hx
Normal file
@@ -0,0 +1,7 @@
|
||||
package classes;
|
||||
class BoolProperty {
|
||||
public var property:Bool;
|
||||
public function new(v:Bool=false):Void {
|
||||
property = v;
|
||||
}
|
||||
}
|
5
cloner/test/classes/ClassProperty.hx
Normal file
5
cloner/test/classes/ClassProperty.hx
Normal file
@@ -0,0 +1,5 @@
|
||||
package classes;
|
||||
class ClassProperty {
|
||||
public var property:Dynamic;
|
||||
public function new():Void {}
|
||||
}
|
5
cloner/test/classes/ClassTypeProperty.hx
Normal file
5
cloner/test/classes/ClassTypeProperty.hx
Normal file
@@ -0,0 +1,5 @@
|
||||
package classes;
|
||||
class ClassTypeProperty {
|
||||
public var property:Class<Dynamic>;
|
||||
public function new():Void {}
|
||||
}
|
4
cloner/test/classes/NullClass.hx
Normal file
4
cloner/test/classes/NullClass.hx
Normal file
@@ -0,0 +1,4 @@
|
||||
package classes;
|
||||
class NullClass {
|
||||
public function new():Void {}
|
||||
}
|
15
cloner/test/compile.hxml
Normal file
15
cloner/test/compile.hxml
Normal file
@@ -0,0 +1,15 @@
|
||||
-cp .
|
||||
-cp ../src
|
||||
-lib hx3compat
|
||||
-main TestMain
|
||||
-debug
|
||||
-cpp ../bin/test
|
||||
|
||||
--next
|
||||
|
||||
-cp .
|
||||
-cp ../src
|
||||
-lib hx3compat
|
||||
-main TestMain
|
||||
-debug
|
||||
-neko ../bin/test/TestMain.n
|
Reference in New Issue
Block a user