1 package net.systemeD.halcyon.connection {
3 import flash.events.TimerEvent;
4 import flash.utils.Timer;
6 import net.systemeD.halcyon.connection.*;
7 import net.systemeD.halcyon.connection.actions.*;
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
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 = [];
20 public function EntityCollection(entities:Array) {
21 super(-1, 0, {}, true, -1, "");
24 //To avoid firing on every contained entity, we wait some short time before firing the events
25 tagChangedTimer = new Timer(50, 1); tagChangedTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
26 addedToRelationTimer = new Timer(50, 1); addedToRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
27 removedFromRelationTimer= new Timer(50, 1); removedFromRelationTimer.addEventListener(TimerEvent.TIMER, onTimerFinished, false, 0, true);
28 for each(var entity:Entity in _entities) {
29 entity.addEventListener(Connection.TAG_CHANGED, onTagChanged, false, 0, true);
30 entity.addEventListener(Connection.ADDED_TO_RELATION, onAddedToRelation, false, 0, true);
31 entity.addEventListener(Connection.REMOVED_FROM_RELATION, onRemovedFromRelation, false, 0, true);
35 public function get entities():Array {
39 private function onTagChanged(event:TagEvent):void {
40 if(tagChangedTimer.running) return;
41 delayedEvents.push(new TagEvent(Connection.TAG_CHANGED, this, event.key, event.key, event.oldValue, event.newValue));
42 tagChangedTimer.start();
45 private function onAddedToRelation(event:RelationMemberEvent):void {
46 if(addedToRelationTimer.running) return;
47 delayedEvents.push(new RelationMemberEvent(Connection.ADDED_TO_RELATION, this, event.relation, event.index));
48 addedToRelationTimer.start();
51 private function onRemovedFromRelation(event:RelationMemberEvent):void {
52 if(removedFromRelationTimer.running) return;
53 delayedEvents.push(new RelationMemberEvent(Connection.REMOVED_FROM_RELATION, this, event.relation, event.index));
54 removedFromRelationTimer.start();
57 private function onTimerFinished(event:TimerEvent):void {
58 dispatchEvent(delayedEvents.shift());
62 // Tag-handling methods
64 private function getMergedTags():Object {
65 //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
66 var differentMarker:String = "<different>";
67 var mergedTags:Object = _entities[0].getTagsCopy();
68 for each(var entity:Entity in _entities) {
69 var entityTags:Object = entity.getTagsHash();
70 for(var key:String in entityTags) {
71 var value:String = entityTags[key];
72 if(mergedTags[key] == null || mergedTags[key] != value) {
73 mergedTags[key] = differentMarker;
76 for(var mergedKey:String in mergedTags) {
77 var mergedValue:String = mergedTags[mergedKey];
78 if(entityTags[mergedKey] == null || entityTags[mergedKey] != mergedValue) {
79 mergedTags[mergedKey] = differentMarker;
86 public override function hasTags():Boolean {
87 for (var key:String in getMergedTags())
92 public override function hasInterestingTags():Boolean {
93 for (var key:String in getMergedTags()) {
94 if (key != "attribution" && key != "created_by" && key != "source" && key.indexOf('tiger:') != 0) {
101 public override function isUneditedTiger():Boolean {
105 public override function getTag(key:String):String {
106 return getMergedTags()[key];
109 public override function tagIs(key:String,value:String):Boolean {
110 if (!getMergedTags[key]) { return false; }
111 return getMergedTags[key]==value;
114 public override function setTag(key:String, value:String, performAction:Function):void {
115 var oldValue:String = getMergedTags()[key];
116 var undoAction:CompositeUndoableAction = new CompositeUndoableAction("set_tag_entity_collection");
117 for each (var entity:Entity in _entities) {
118 undoAction.push(new SetTagAction(entity, key, value));
120 performAction(undoAction);
123 public override function renameTag(oldKey:String, newKey:String, performAction:Function):void {
124 var undoAction:CompositeUndoableAction = new CompositeUndoableAction("rename_tag_entity_collection");
125 for each (var entity:Entity in _entities) {
126 undoAction.push(new SetTagKeyAction(entity, oldKey, newKey));
128 performAction(undoAction);
131 public override function getTagList():TagList {
132 return new TagList(getMergedTags());
135 public override function getTagsCopy():Object {
136 return getMergedTags();
139 public override function getTagsHash():Object {
140 return getMergedTags();
143 public override function getTagArray():Array {
145 var mergedTags:Object = getMergedTags();
146 for (var key:String in mergedTags) {
147 copy.push(new Tag(this, key, mergedTags[key]));
152 public override function get parentRelations():Array {
153 var relations:Array = [];
154 for each (var entity:Entity in _entities) {
155 for each (var relation:Relation in entity.parentRelations) {
156 if (relations.indexOf(relation)==-1) relations.push(relation);
162 public override function getRelationMemberships():Array {
163 var relations:Object = {};
164 for each (var entity:Entity in _entities) {
165 for each (var rel:Relation in entity.parentRelations) {
166 for each(var memberIndex:int in rel.findEntityMemberIndexes(entity)) {
167 var role:String=rel.getMember(memberIndex).role;
168 if (!relations[rel.id]) {
169 relations[rel.id]= { role: role, relation: rel, distinctCount: 0};
170 } else if (relations[rel.id].role!=role) {
171 relations[rel.id].role="<different>";
174 relations[rel.id].distinctCount++;
177 var memberships:Array = [];
178 for (var id:String in relations) {
180 relation: relations[id].relation,
182 role: relations[id].role,
183 description: relations[id].relation.getDescription(),
184 universal: relations[id].distinctCount==_entities.length,
188 // ** FIXME: .universal should be shown in the tag panel
191 // Clean/dirty methods
193 public override function get isDirty():Boolean {
194 for each (var entity:Entity in _entities) {
195 if(entity.isDirty) return true;
200 public override function markClean():void {
201 for each (var entity:Entity in _entities) {
206 internal override function markDirty():void {
207 for each (var entity:Entity in _entities) {
215 public override function getDescription():String {
216 var basic:String=this.getType();
217 var mergedTags:Object = getMergedTags();
218 if (mergedTags['ref'] && mergedTags['name']) { return mergedTags['ref']+' '+mergedTags['name']+' ('+basic+')'; }
219 if (mergedTags['ref']) { return mergedTags['ref']+' ('+basic+')'; }
220 if (mergedTags['name']) { return mergedTags['name']+' ('+basic+')'; }
224 public override function getType():String {
225 var entityType:String = '';
226 for each (var entity:Entity in _entities) {
227 if(entityType == '') entityType = entity.getType();
228 else if(entityType != entity.getType()) {