package org.libspark.common.support { import flash.utils.ByteArray; import flash.utils.Dictionary; import org.libspark.common.store.IStorable; import org.libspark.common.store.DictionaryedWritableStore; /** * IStorable を実装したクラスに対して IComparable#isDeepEqual メソッドの実装をサポートします. */ public class DeepEqualSupport extends EqualSupport { public function DeepEqualSupport() { } private var _collecteds:Array; private var _comparings:Dictionary; protected function get collecteds():Array { return _collecteds; } protected function set collecteds(value:Array):void { _collecteds = value; } protected function pushCollected(collected:Dictionary):void { collecteds.push(collected); } protected function popCollected():void { collecteds.pop(); } override protected function get collected():Dictionary { return collecteds[collecteds.length - 1]; } protected function get comparings():Dictionary { return _comparings; } protected function set comparings(value:Dictionary):void { _comparings = value; } protected function registerComparing(a:Object, b:Object):void { internalRegisterComparing(a, b); internalRegisterComparing(b, a); } protected function internalRegisterComparing(a:Object, b:Object):void { var list:Array = comparings[a] as Array; if (list == null) { list = []; comparings[a] = list; } list.push(b); } protected function isComparing(a:Object, b:Object):Boolean { return internalIsComparing(a, b) || internalIsComparing(b, a); } protected function internalIsComparing(a:Object, b:Object):Boolean { var list:Array = comparings[a] as Array; if (list == null) { return false; } if (list.indexOf(b) >= 0) { return true; } return false; } override public function compare(a:IStorable, b:IStorable):void { collecteds = []; comparings = new Dictionary(); setResult(true); deepCompare(a, b); } protected function deepCompare(a:IStorable, b:IStorable):void { if (isComparing(a, b)) { return; } registerComparing(a, b); if (a == b) { return; } var collector:DictionaryedWritableStore = new DictionaryedWritableStore(); b.store(collector); pushCollected(collector.store); a.store(this); popCollected(); } protected function deepCompareArray(a:Array, b:Array):void { if (isComparing(a, b)) { return; } registerComparing(a, b); if (a == b) { return; } if (a.length != b.length) { setResult(false); return; } var l:uint = a.length; for (var i:uint = 0; i < l; ++i) { bestDeepCompare(a[i], b[i]); if (!result) { break; } } } protected function deepCompareByteArray(a:ByteArray, b:ByteArray):void { if (isComparing(a, b)) { return; } registerComparing(a, b); if (a == b) { return; } if (a.length != b.length) { setResult(false); return; } var apos:uint = a.position; var bpos:uint = b.position; a.position = 0; b.position = 0; while (a.bytesAvailable > 0) { if (a.readByte() != b.readByte()) { setResult(false); break; } } a.position = apos; b.position = bpos; } protected function deepCompareDictionary(a:Dictionary, b:Dictionary):void { if (isComparing(a, b)) { return; } registerComparing(a, b); if (a == b) { return; } var key:*; var keys:Array = []; for (key in b) { keys.push(key); } for (key in a) { var i:int = keys.indexOf(key); if (i == -1) { setResult(false); return; } keys.splice(i, 1); bestDeepCompare(a[key],b[key]); if (!result) { return; } } if (keys.length != 0) { setResult(false); } } protected function bestDeepCompare(a:Object, b:Object):void { if (a == null) { if (b != null) { setResult(false); } return; } else { if (b == null) { setResult(false); return; } } if (a == b) { return; } if (a is IStorable && b is IStorable) { deepCompare(a as IStorable, b as IStorable); return; } if (a is Array && b is Array) { deepCompareArray(a as Array, b as Array); return; } if (a is ByteArray && b is ByteArray) { deepCompareByteArray(a as ByteArray, b as ByteArray); return; } if (a is Dictionary && b is Dictionary) { deepCompareDictionary(a as Dictionary, b as Dictionary); return; } setResult(false); } override public function writeStorable(key:String, value:IStorable):void { if (!result) { return; } if (!(key in collected)) { setResult(false); return; } var b:IStorable = collected[key] as IStorable; if (value == null) { if (b != null) { setResult(false); } return; } else { if (b == null) { setResult(false); return; } } deepCompare(value, b); } override public function writeArray(key:String, value:Array):void { if (!result) { return; } if (!(key in collected)) { setResult(false); return; } var b:Array = collected[key] as Array; if (value == null) { if (b != null) { setResult(false); } return; } else { if (b == null) { setResult(false); return; } } deepCompareArray(value, b); } override public function writeByteArray(key:String, value:ByteArray):void { if (!result) { return; } if (!(key in collected)) { setResult(false); return; } var b:ByteArray = collected[key] as ByteArray; if (value == null) { if (b != null) { setResult(false); } return; } else { if (b == null) { setResult(false); return; } } deepCompareByteArray(value, b); } override public function writeDictionary(key:String, value:Dictionary):void { if (!result) { return; } if (!(key in collected)) { setResult(false); return; } var b:Dictionary = collected[key] as Dictionary; if (value == null) { if (b != null) { setResult(false); } return; } else { if (b == null) { setResult(false); return; } } deepCompareDictionary(value, b); } } }