Squashed 'cloner/' content from commit 79ff709
git-subtree-dir: cloner git-subtree-split: 79ff709d0060b967d3508b6abfe194bedfb63f1b
This commit is contained in:
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