Merge branch 'master' into history
[potlatch2.git] / net / systemeD / halcyon / connection / EntityCollection.as
1 package net.systemeD.halcyon.connection {
2
3         import flash.events.TimerEvent;
4         import flash.utils.Timer;
5     
6         import net.systemeD.halcyon.connection.*;
7     import net.systemeD.halcyon.connection.actions.*;
8
9         // ** FIXME:
10         // - Can we rework the ControllerStates so they work with this (rather than just a raw Array)?
11         // - It may be possible to generalise the event timer code into a tidier "event grouping" class of some sort
12
13     public class EntityCollection extends Entity {
14                 private var _entities:Array;
15                 private var tagChangedTimer:Timer;
16                 private var addedToRelationTimer:Timer;
17                 private var removedFromRelationTimer:Timer;
18                 private var delayedEvents:Array = [];
19                 private static const DIFFERENT:String = "<different>";
20
21         public function EntityCollection(entities:Array) {
22                         var conn:Connection=entities[0].connection;
23                         // ** FIXME: this really is a very nasty way of finding the connection
24                         
25                         super(conn, -1, 0, {}, true, NaN, null, null);
26             _entities = entities;
27                         
28                         //To avoid firing on every contained entity, we wait some short time before firing the events
29                         tagChangedTimer         = new Timer(50, 1); tagChangedTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
30                         addedToRelationTimer    = new Timer(50, 1); addedToRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
31                         removedFromRelationTimer= new Timer(50, 1); removedFromRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
32                         for each(var entity:Entity in _entities) {
33                                 entity.addEventListener(Connection.TAG_CHANGED, onTagChanged, false, 0, true);
34                                 entity.addEventListener(Connection.ADDED_TO_RELATION, onAddedToRelation, false, 0, true);
35                                 entity.addEventListener(Connection.REMOVED_FROM_RELATION, onRemovedFromRelation, false, 0, true);
36                         }
37         }
38                 
39                 public function releaseListeners():void {
40                         for each(var entity:Entity in _entities) {
41                                 entity.removeEventListener(Connection.TAG_CHANGED, onTagChanged);
42                                 entity.removeEventListener(Connection.ADDED_TO_RELATION, onAddedToRelation);
43                                 entity.removeEventListener(Connection.REMOVED_FROM_RELATION, onRemovedFromRelation);
44                         }
45                 }
46                 
47                 public override function get entities():Array {
48                         return _entities;
49                 }
50                 
51                 private function onTagChanged(event:TagEvent):void {
52                         if(tagChangedTimer.running) return;
53                         delayedEvents.push(new TagEvent(Connection.TAG_CHANGED, this, event.key, event.key, event.oldValue, event.newValue));
54                         tagChangedTimer.start();
55                 }
56                 
57                 private function onAddedToRelation(event:RelationMemberEvent):void {
58                         if(addedToRelationTimer.running) return;
59                         delayedEvents.push(new RelationMemberEvent(Connection.ADDED_TO_RELATION, this, event.relation, event.index));
60                         addedToRelationTimer.start();
61                 }
62                 
63                 private function onRemovedFromRelation(event:RelationMemberEvent):void {
64                         if(removedFromRelationTimer.running) return;
65                         delayedEvents.push(new RelationMemberEvent(Connection.REMOVED_FROM_RELATION, this, event.relation, event.index));
66                         removedFromRelationTimer.start();
67                 }
68                 
69                 private function onTimerFinished(event:TimerEvent):void { 
70                         dispatchEvent(delayedEvents.shift());
71                 }
72                 
73                 
74                 // Tag-handling methods
75                 
76                 private function getMergedTags():Object {
77                         //Builds an object with tags of all entities in this collection. If the value of a tag differs or is not set in all entities, value is marked
78                         var mergedTags:Object = _entities[0].getTagsCopy();
79                         for each(var entity:Entity in _entities) {
80                                 var entityTags:Object = entity.getTagsHash();
81                                 for(var key:String in entityTags) {
82                                         var value:String = entityTags[key];
83                                         if(mergedTags[key] == null || mergedTags[key] != value) {
84                                                 mergedTags[key] = DIFFERENT;
85                                         }
86                                 }
87                                 for(var mergedKey:String in mergedTags) {
88                                         var mergedValue:String = mergedTags[mergedKey];
89                                         if(entityTags[mergedKey] == null || entityTags[mergedKey] != mergedValue) {
90                                                 mergedTags[mergedKey] = DIFFERENT;
91                                         }
92                                 }
93                         }
94                         return mergedTags;
95                 }
96
97         public override function hasTags():Boolean {
98                         for (var key:String in getMergedTags())
99                 return true;
100             return false;
101         }
102
103         public override function hasInterestingTags():Boolean {
104                         for (var key:String in getMergedTags()) {
105               if (key != "attribution" && key != "created_by" && key != "source" && key.indexOf('tiger:') != 0) {
106                 return true;
107               }
108             }
109             return false;
110         }
111
112         public override function isUneditedTiger():Boolean {
113             return false;
114         }
115
116         public override function getTag(key:String):String {
117                         return getMergedTags()[key];
118         }
119
120                 public override function tagIs(key:String,value:String):Boolean {
121                         if (!getMergedTags[key]) { return false; }
122                         return getMergedTags[key]==value;
123                 }
124                 
125                 public override function setTag(key:String, value:String, performAction:Function):void {
126             if (value==DIFFERENT) return;
127                         var oldValue:String = getMergedTags()[key];     
128                         var undoAction:CompositeUndoableAction = new CompositeUndoableAction("set_tag_entity_collection");
129                         for each (var entity:Entity in _entities) {
130                                 undoAction.push(new SetTagAction(entity, key, value));
131                         }
132             performAction(undoAction);
133         }
134
135         public override function renameTag(oldKey:String, newKey:String, performAction:Function):void {
136                         var undoAction:CompositeUndoableAction = new CompositeUndoableAction("rename_tag_entity_collection");
137                         for each (var entity:Entity in _entities) {
138                                 if (entity.getTag(oldKey)) {
139                                         undoAction.push(new SetTagKeyAction(entity, oldKey, newKey));
140                                 }
141                         }
142             performAction(undoAction);
143         }
144
145         public override function getTagList():TagList {
146             return new TagList(getMergedTags());
147         }
148
149         public override function getTagsCopy():Object {
150                         return getMergedTags();
151         }
152
153                 public override function getTagsHash():Object {
154                         return getMergedTags();
155                 }
156
157         public override function getTagArray():Array {
158             var copy:Array = [];
159                         var mergedTags:Object = getMergedTags();
160             for (var key:String in mergedTags) {
161                 copy.push(new Tag(this, key, mergedTags[key]));
162                         }
163             return copy;
164         }
165
166                 public override function get parentRelations():Array {
167                         var relations:Array = [];
168                         for each (var entity:Entity in _entities) {
169                                 for each (var relation:Relation in entity.parentRelations) {
170                                         if (relations.indexOf(relation)==-1) relations.push(relation);
171                                 }
172                         }
173                         return relations;
174                 }
175
176                 public override function getRelationMemberships():Array {
177                         var relations:Object = {};
178                         for each (var entity:Entity in _entities) {
179                                 for each (var rel:Relation in entity.parentRelations) {
180                                         for each(var memberIndex:int in rel.findEntityMemberIndexes(entity)) {
181                                                 var role:String=rel.getMember(memberIndex).role;
182                                                 if (!relations[rel.id]) {
183                                                         relations[rel.id]= { role: role, relation: rel, distinctCount: 0};
184                                                 } else if (relations[rel.id].role!=role) {
185                                                         relations[rel.id].role=DIFFERENT;
186                                                 }
187                                         }
188                                         relations[rel.id].distinctCount++;
189                                 }
190                         }
191                         var memberships:Array = [];
192                         for (var id:String in relations) {
193                                 memberships.push({
194                                         relation: relations[id].relation,
195                                         id: Number(id),
196                                         role: relations[id].role,
197                                         description: relations[id].relation.getDescription(),
198                                         universal: relations[id].distinctCount==_entities.length,
199                                         id_idx: id });
200                         }
201                         return memberships;
202                         // ** FIXME: .universal should be shown in the tag panel
203                 }
204
205                 // Clean/dirty methods
206
207         public override function get isDirty():Boolean {
208             for each (var entity:Entity in _entities) {
209                                 if(entity.isDirty) return true;
210                         }
211                         return false;
212         }
213
214         public override function markClean():void {
215              for each (var entity:Entity in _entities) {
216                                 entity.markClean();
217                         }
218         }
219
220         internal override function markDirty():void {
221             for each (var entity:Entity in _entities) {
222                                 entity.markDirty();
223                         }
224         }
225         
226         
227                 // Others
228         
229                 public override function getDescription():String {
230                         var basic:String=this.getType();
231                         var mergedTags:Object = getMergedTags();
232                         if (mergedTags['ref'] && mergedTags['name']) { return mergedTags['ref']+' '+mergedTags['name']+' ('+basic+')'; }
233                         if (mergedTags['ref']) { return mergedTags['ref']+' ('+basic+')'; }
234                         if (mergedTags['name']) { return mergedTags['name']+' ('+basic+')'; }
235                         return basic;
236                 }
237
238         public override function getType():String {
239                         var entityType:String = '';
240                          for each (var entity:Entity in _entities) {
241                                 if(entityType == '') entityType = entity.getType();
242                                 else if(entityType != entity.getType()) {
243                                         entityType = '';
244                                         break;
245                                 }
246                         }
247             return entityType;
248         }
249
250     }
251
252 }
253