/src/org/robotlegs/base/MediatorMap.as

https://github.com/btspoony/MyRobotlegsBundle · ActionScript · 356 lines · 218 code · 39 blank · 99 comment · 47 complexity · d7025a54132f6a57dcf6d02eab9925db MD5 · raw file

  1. /*
  2. * Copyright (c) 2009, 2010 the original author or authors
  3. *
  4. * Permission is hereby granted to use, modify, and distribute this file
  5. * in accordance with the terms of the license agreement accompanying it.
  6. */
  7. package org.robotlegs.base
  8. {
  9. import flash.display.DisplayObject;
  10. import flash.display.DisplayObjectContainer;
  11. import flash.display.Sprite;
  12. import flash.events.Event;
  13. import flash.utils.Dictionary;
  14. import flash.utils.getQualifiedClassName;
  15. import org.robotlegs.core.IInjector;
  16. import org.robotlegs.core.IMediator;
  17. import org.robotlegs.core.IMediatorMap;
  18. import org.robotlegs.core.IReflector;
  19. /**
  20. * An abstract <code>IMediatorMap</code> implementation
  21. */
  22. public class MediatorMap extends ViewMapBase implements IMediatorMap
  23. {
  24. /**
  25. * @private
  26. */
  27. protected static const enterFrameDispatcher:Sprite = new Sprite();
  28. /**
  29. * @private
  30. */
  31. protected var mediatorByView:Dictionary;
  32. /**
  33. * @private
  34. */
  35. protected var mappingConfigByView:Dictionary;
  36. /**
  37. * @private
  38. */
  39. protected var mappingConfigByViewClassName:Dictionary;
  40. /**
  41. * @private
  42. */
  43. protected var mediatorsMarkedForRemoval:Dictionary;
  44. /**
  45. * @private
  46. */
  47. protected var hasMediatorsMarkedForRemoval:Boolean;
  48. /**
  49. * @private
  50. */
  51. protected var reflector:IReflector;
  52. //---------------------------------------------------------------------
  53. // Constructor
  54. //---------------------------------------------------------------------
  55. /**
  56. * Creates a new <code>MediatorMap</code> object
  57. *
  58. * @param contextView The root view node of the context. The map will listen for ADDED_TO_STAGE events on this node
  59. * @param injector An <code>IInjector</code> to use for this context
  60. * @param reflector An <code>IReflector</code> to use for this context
  61. */
  62. public function MediatorMap(contextView:DisplayObjectContainer, injector:IInjector, reflector:IReflector)
  63. {
  64. super(contextView, injector);
  65. this.reflector = reflector;
  66. // mappings - if you can do it with fewer dictionaries you get a prize
  67. this.mediatorByView = new Dictionary(true);
  68. this.mappingConfigByView = new Dictionary(true);
  69. this.mappingConfigByViewClassName = new Dictionary(false);
  70. this.mediatorsMarkedForRemoval = new Dictionary(false);
  71. }
  72. //---------------------------------------------------------------------
  73. // API
  74. //---------------------------------------------------------------------
  75. /**
  76. * @inheritDoc
  77. */
  78. public function mapView(viewClassOrName:*, mediatorClass:Class, injectViewAs:* = null, autoCreate:Boolean = true, autoRemove:Boolean = true):void
  79. {
  80. var viewClassName:String = reflector.getFQCN(viewClassOrName);
  81. if (mappingConfigByViewClassName[viewClassName] != null)
  82. throw new ContextError(ContextError.E_MEDIATORMAP_OVR + ' - ' + mediatorClass);
  83. if (reflector.classExtendsOrImplements(mediatorClass, IMediator) == false)
  84. throw new ContextError(ContextError.E_MEDIATORMAP_NOIMPL + ' - ' + mediatorClass);
  85. var config:MappingConfig = new MappingConfig();
  86. config.mediatorClass = mediatorClass;
  87. config.autoCreate = autoCreate;
  88. config.autoRemove = autoRemove;
  89. if (injectViewAs)
  90. {
  91. if (injectViewAs is Array)
  92. {
  93. config.typedViewClasses = (injectViewAs as Array).concat();
  94. }
  95. else if (injectViewAs is Class)
  96. {
  97. config.typedViewClasses = [injectViewAs];
  98. }
  99. }
  100. else if (viewClassOrName is Class)
  101. {
  102. config.typedViewClasses = [viewClassOrName];
  103. }
  104. mappingConfigByViewClassName[viewClassName] = config;
  105. if (autoCreate || autoRemove)
  106. {
  107. viewListenerCount++;
  108. if (viewListenerCount == 1)
  109. addListeners();
  110. }
  111. // This was a bad idea - causes unexpected eager instantiation of object graph
  112. if (autoCreate && contextView && (viewClassName == getQualifiedClassName(contextView) ))
  113. createMediatorUsing(contextView, viewClassName, config);
  114. }
  115. /**
  116. * @inheritDoc
  117. */
  118. public function unmapView(viewClassOrName:*):void
  119. {
  120. var viewClassName:String = reflector.getFQCN(viewClassOrName);
  121. var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
  122. if (config && (config.autoCreate || config.autoRemove))
  123. {
  124. viewListenerCount--;
  125. if (viewListenerCount == 0)
  126. removeListeners();
  127. }
  128. delete mappingConfigByViewClassName[viewClassName];
  129. }
  130. /**
  131. * @inheritDoc
  132. */
  133. public function createMediator(viewComponent:Object):IMediator
  134. {
  135. return createMediatorUsing(viewComponent);
  136. }
  137. /**
  138. * @inheritDoc
  139. */
  140. public function registerMediator(viewComponent:Object, mediator:IMediator):void
  141. {
  142. var mediatorClass:Class = reflector.getClass(mediator);
  143. injector.hasMapping(mediatorClass) && injector.unmap(mediatorClass);
  144. injector.mapValue(mediatorClass, mediator);
  145. mediatorByView[viewComponent] = mediator;
  146. mappingConfigByView[viewComponent] = mappingConfigByViewClassName[getQualifiedClassName(viewComponent)];
  147. mediator.setViewComponent(viewComponent);
  148. mediator.preRegister();
  149. }
  150. /**
  151. * @inheritDoc
  152. */
  153. public function removeMediator(mediator:IMediator):IMediator
  154. {
  155. if (mediator)
  156. {
  157. var viewComponent:Object = mediator.getViewComponent();
  158. var mediatorClass:Class = reflector.getClass(mediator);
  159. delete mediatorByView[viewComponent];
  160. delete mappingConfigByView[viewComponent];
  161. mediator.preRemove();
  162. mediator.setViewComponent(null);
  163. injector.hasMapping(mediatorClass) && injector.unmap(mediatorClass);
  164. }
  165. return mediator;
  166. }
  167. /**
  168. * @inheritDoc
  169. */
  170. public function removeMediatorByView(viewComponent:Object):IMediator
  171. {
  172. return removeMediator(retrieveMediator(viewComponent));
  173. }
  174. /**
  175. * @inheritDoc
  176. */
  177. public function retrieveMediator(viewComponent:Object):IMediator
  178. {
  179. return mediatorByView[viewComponent];
  180. }
  181. /**
  182. * @inheritDoc
  183. */
  184. public function hasMapping(viewClassOrName:*):Boolean
  185. {
  186. var viewClassName:String = reflector.getFQCN(viewClassOrName);
  187. return (mappingConfigByViewClassName[viewClassName] != null);
  188. }
  189. /**
  190. * @inheritDoc
  191. */
  192. public function hasMediatorForView(viewComponent:Object):Boolean
  193. {
  194. return mediatorByView[viewComponent] != null;
  195. }
  196. /**
  197. * @inheritDoc
  198. */
  199. public function hasMediator(mediator:IMediator):Boolean
  200. {
  201. for each (var med:IMediator in mediatorByView)
  202. if (med == mediator)
  203. return true;
  204. return false;
  205. }
  206. //---------------------------------------------------------------------
  207. // Internal
  208. //---------------------------------------------------------------------
  209. /**
  210. * @private
  211. */
  212. protected override function addListeners():void
  213. {
  214. if (contextView && enabled)
  215. {
  216. contextView.addEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture, 0, true);
  217. contextView.addEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture, 0, true);
  218. }
  219. }
  220. /**
  221. * @private
  222. */
  223. protected override function removeListeners():void
  224. {
  225. if (contextView)
  226. {
  227. contextView.removeEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture);
  228. contextView.removeEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture);
  229. }
  230. }
  231. /**
  232. * @private
  233. */
  234. protected override function onViewAdded(e:Event):void
  235. {
  236. if (mediatorsMarkedForRemoval[e.target])
  237. {
  238. delete mediatorsMarkedForRemoval[e.target];
  239. return;
  240. }
  241. var viewClassName:String = getQualifiedClassName(e.target);
  242. var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
  243. if (config && config.autoCreate)
  244. createMediatorUsing(e.target, viewClassName, config);
  245. }
  246. /**
  247. * @private
  248. */
  249. protected function createMediatorUsing(viewComponent:Object, viewClassName:String = '', config:MappingConfig = null):IMediator
  250. {
  251. var mediator:IMediator = mediatorByView[viewComponent];
  252. if (mediator == null)
  253. {
  254. viewClassName ||= getQualifiedClassName(viewComponent);
  255. config ||= mappingConfigByViewClassName[viewClassName];
  256. if (config)
  257. {
  258. for each (var claxx:Class in config.typedViewClasses)
  259. {
  260. injector.mapValue(claxx, viewComponent);
  261. }
  262. mediator = createMediatorInstance(config.mediatorClass);
  263. for each (var clazz:Class in config.typedViewClasses)
  264. {
  265. injector.unmap(clazz);
  266. }
  267. registerMediator(viewComponent, mediator);
  268. }
  269. }
  270. return mediator;
  271. }
  272. /**
  273. * Create a mediator instance
  274. */
  275. protected function createMediatorInstance(mediatorClass:Class):IMediator
  276. {
  277. return injector.instantiate(mediatorClass);
  278. }
  279. /**
  280. * Flex framework work-around part #5
  281. */
  282. protected function onViewRemoved(e:Event):void
  283. {
  284. var config:MappingConfig = mappingConfigByView[e.target];
  285. if (config && config.autoRemove)
  286. {
  287. mediatorsMarkedForRemoval[e.target] = e.target;
  288. if (!hasMediatorsMarkedForRemoval)
  289. {
  290. hasMediatorsMarkedForRemoval = true;
  291. enterFrameDispatcher.addEventListener(Event.ENTER_FRAME, removeMediatorLater);
  292. }
  293. }
  294. }
  295. /**
  296. * Flex framework work-around part #6
  297. */
  298. protected function removeMediatorLater(event:Event):void
  299. {
  300. enterFrameDispatcher.removeEventListener(Event.ENTER_FRAME, removeMediatorLater);
  301. for each (var view:DisplayObject in mediatorsMarkedForRemoval)
  302. {
  303. if (!view.stage)
  304. removeMediatorByView(view);
  305. delete mediatorsMarkedForRemoval[view];
  306. }
  307. hasMediatorsMarkedForRemoval = false;
  308. }
  309. }
  310. }
  311. class MappingConfig
  312. {
  313. public var mediatorClass:Class;
  314. public var typedViewClasses:Array;
  315. public var autoCreate:Boolean;
  316. public var autoRemove:Boolean;
  317. }