placeholders for second half of the code (will need a small API update before this...
[potlatch2.git] / net / systemeD / halcyon / connection / Entity.as
1 package net.systemeD.halcyon.connection {
2
3     import flash.events.EventDispatcher;
4     import flash.utils.Dictionary;
5     
6     import net.systemeD.halcyon.connection.actions.*;
7
8     public class Entity extends EventDispatcher {
9         private var _id:Number;
10         private var _version:uint;
11         private var _uid:Number;
12         private var _timestamp:String;
13         private var tags:Object = {};
14         private var modified:Boolean = false;
15                 private var _loaded:Boolean = true;
16                 private var parents:Dictionary = new Dictionary();
17                 public var locked:Boolean = false;                                              // lock against purging when off-screen
18                 public var deleted:Boolean = false;
19 //              public var parentsLoaded:Boolean = true;                                // are all its parents in memory?
20
21         public function Entity(id:Number, version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String) {
22             this._id = id;
23             this._version = version;
24             this._uid = uid;
25             this._timestamp = timestamp;
26             this.tags = tags;
27                         this._loaded = loaded;
28             modified = id < 0;
29         }
30
31         public function get id():Number {
32             return _id;
33         }
34
35         public function get version():uint {
36             return _version;
37         }
38
39         public function get uid():Number {
40             return _uid;
41         }
42
43         public function get loaded():Boolean {
44             return _loaded;
45         }
46
47                 public function get timestamp():String {
48                         return _timestamp;
49                 }
50
51                 public function updateEntityProperties(version:uint, tags:Object, loaded:Boolean, uid:Number, timestamp:String):void {
52                         _version=version; this.tags=tags; _loaded=loaded; _uid = uid; _timestamp = timestamp;
53                         deleted=false;
54                 }
55
56                 public function renumber(newID:Number, newVersion:uint):void {
57                         this._id = newID;
58                         this._version = newVersion;
59                 }
60
61                 // Tag-handling methods
62
63         public function hasTags():Boolean {
64             for (var key:String in tags)
65                 return true;
66             return false;
67         }
68
69         public function hasInterestingTags():Boolean {
70             for (var key:String in tags) {
71               if (key != "attribution" && key != "created_by" && key != "source" && key.indexOf('tiger:') != 0) {
72                 //trace(key);
73                 return true;
74               }
75             }
76             return false;
77         }
78
79         public function isUneditedTiger():Boolean {
80             // todo: make this match the rules from the tiger edited map
81             // http://github.com/MapQuest/TIGER-Edited-map/blob/master/inc/layer-tiger.xml.inc
82             if (this is Way && (uid == 7168 || uid == 15169 || uid == 20587)) {//todo fixme etc
83               return true;
84             }
85             return false;
86         }
87
88         public function getTag(key:String):String {
89             return tags[key];
90         }
91
92                 public function tagIs(key:String,value:String):Boolean {
93                         if (!tags[key]) { return false; }
94                         return tags[key]==value;
95                 }
96         
97         public function setTag(key:String, value:String, performAction:Function):void {
98             performAction(new SetTagAction(this, key, value));
99         }
100
101         public function renameTag(oldKey:String, newKey:String, performAction:Function):void {
102             performAction(new SetTagKeyAction(this, oldKey, newKey));
103         }
104
105         public function getTagList():TagList {
106             return new TagList(tags);
107         }
108
109         public function getTagsCopy():Object {
110             var copy:Object = {};
111             for (var key:String in tags )
112                 copy[key] = tags[key];
113             return copy;
114         }
115
116                 public function getTagsHash():Object {
117                         // hm, not sure we should be doing this, but for read-only purposes
118                         // it's faster than using getTagsCopy
119                         return tags;
120                 }
121
122         public function getTagArray():Array {
123             var copy:Array = [];
124             for (var key:String in tags )
125                 copy.push(new Tag(this, key, tags[key]));
126             return copy;
127         }
128
129                 // Clean/dirty methods
130
131         public function get isDirty():Boolean {
132             return modified;
133         }
134
135         public function markClean():void {
136             modified = false;
137         }
138
139         internal function markDirty():void {
140             modified = true;
141         }
142
143                 // Delete entity
144                 
145                 public function remove(performAction:Function):void {
146                         // to be overridden
147                 }
148                 
149                 public function isDeleted():Boolean {
150                     return deleted;
151                 }
152                 
153                 public function setDeletedState(isDeleted:Boolean):void {
154                     deleted = isDeleted;
155             if (this is Node) {
156               var n:Node = Node(this);
157               if (isDeleted) {
158                 Connection.getConnection().removeDupe(n);
159               } else {
160                 Connection.getConnection().addDupe(n);
161               }
162             }
163                 }
164                 
165                 internal function isEmpty():Boolean {
166                         return false;   // to be overridden
167                 }
168
169                 public function nullify():void {
170                         // this retains a dummy entity in memory, for entities that we no longer need
171                         // but which are part of a still-in-memory relation
172                         nullifyEntity();
173                 }
174                 
175                 protected function nullifyEntity():void {
176                         // this is the common nullify behaviour for all entity types (we'd call this by super() if ActionScript let us)
177                         _version=0;
178                         _loaded=false;
179                         tags={};
180                 }
181                 
182                 
183                 public function within(left:Number,right:Number,top:Number,bottom:Number):Boolean {
184                         return true;    // to be overridden
185                 }
186
187                 public function removeFromParents(performAction:Function):void {
188                         for (var o:Object in parents) {
189                                 if (o is Relation) { Relation(o).removeMember(this, performAction); }
190                                 else if (o is Way) { Way(o).removeNode(Node(this), performAction); }
191                                 if (o.isEmpty()) { o.remove(performAction); }
192                         }
193                 }
194
195                 // Parent handling
196                 
197                 public function addParent(parent:Entity):void {
198                         parents[parent]=true;
199                         
200                         if ( parent is Relation )
201                             dispatchEvent(new RelationMemberEvent(Connection.ADDED_TO_RELATION, this, parent as Relation, -1));
202                 }
203
204                 public function removeParent(parent:Entity):void {
205                         delete parents[parent];
206
207                         if ( parent is Relation )
208                             dispatchEvent(new RelationMemberEvent(Connection.REMOVED_FROM_RELATION, this, parent as Relation, -1));
209                 }
210                 
211                 public function get parentWays():Array {
212                         var a:Array=[];
213                         for (var o:Object in parents) {
214                                 if (o is Way) { a.push(o); }
215                         }
216                         return a;
217                 }
218
219                 public function get hasParents():Boolean {
220                         for (var o:Object in parents) { return true; }
221                         return false;
222                 }
223                 
224                 public function get hasParentWays():Boolean {
225                         for (var o:Object in parents) {
226                                 if (o is Way) { return true; }
227                         }
228                         return false;
229                 }
230                 
231                 public function get numParentWays():uint {
232                         var i:uint=0;
233                         for (var o:Object in parents) {
234                                 if (o is Way) { i++; }
235                         }
236                         return i;
237                 }
238                 
239                 public function get parentRelations():Array {
240                         var a:Array=[];
241                         for (var o:Object in parents) {
242                                 if (o is Relation) { a.push(o); }
243                         }
244                         return a;
245                 }
246                 
247                 public function findParentRelationsOfType(type:String, role:String=null):Array {
248                         var a:Array=[];
249                         for (var o:Object in parents) {
250                                 if (o is Relation && Relation(o).tagIs('type',type) && (role==null || Relation(o).hasMemberInRole(this,role))) { 
251                                         a.push(o);
252                                 }
253                         }
254                         return a;
255                 }
256                 
257                 public function countParentObjects(within:Object):uint {
258                         var count:uint=0;
259                         for (var o:Object in parents) {
260                                 if (o.getType()==within.entity && o.getTag(within.k)) {
261                                         if (within.v && within.v!=o.getTag(within.k)) { break; }
262                                         if (within.role && !Relation(o).hasMemberInRole(this,within.role)) { break; }
263                                         count++;
264                                 }
265                         }
266                         return count;
267                 }
268                 
269                 public function get parentObjects():Array {
270                         var a:Array=[];
271                         for (var o:Object in parents) { a.push(o); }
272                         return a;
273                 }
274                 
275                 public function hasParent(entity:Entity):Boolean {
276             return parents[entity] == true;
277         }
278
279                 public function get memberships():Array {
280                         var list:Array=[];
281                         for (var o:Object in parents) {
282                                 if (o is Relation) {
283                                         for (var i:uint=0; i<o.length; i++) {
284                                                 if (o.getMember(i).entity==this) {
285                                                         list.push( { relation:o, position:i, role: o.getMember(i).role } );
286                                                 }
287                                         }
288                                 }
289                         }
290             // it's useful to return in a sorted order, even if the relations are interleaved
291             // e.g. [{r0 p1},{r1 p1},{r0 p4}]
292                         return list.sortOn("position"); 
293                 }
294
295                 // Resume/suspend redraw
296                 
297                 public function suspend():void {
298                         dispatchEvent(new EntityEvent(Connection.SUSPEND_REDRAW, this));
299                 }
300                 
301                 public function resume():void {
302                         dispatchEvent(new EntityEvent(Connection.RESUME_REDRAW, this));
303                 }
304
305                 // To be overridden
306
307                 public function getDescription():String {
308                         var basic:String=this.getType()+" "+_id;
309                         if (tags['ref'] && tags['name']) { return tags['ref']+' '+tags['name']+' ('+basic+')'; }
310                         if (tags['ref']) { return tags['ref']+' ('+basic+')'; }
311                         if (tags['name']) { return tags['name']+' ('+basic+')'; }
312                         return basic;
313                 }
314
315         public function getType():String {
316             return '';
317         }
318
319     }
320
321 }
322