Bugfix: You need to have the entity before you can see if it's deleted
[potlatch2.git] / net / systemeD / halcyon / connection / XMLConnection.as
1 package net.systemeD.halcyon.connection {
2
3     import flash.events.*;
4
5         import flash.system.Security;
6         import flash.net.*;
7     import org.iotashan.oauth.*;
8
9         import net.systemeD.halcyon.Globals;
10
11         public class XMLConnection extends Connection {
12
13         //public var readConnection:NetConnection;
14
15                 public function XMLConnection() {
16
17                         if (Connection.policyURL!='')
18                 Security.loadPolicyFile(Connection.policyURL);
19             var oauthPolicy:String = Connection.getParam("oauth_policy", "");
20             if ( oauthPolicy != "" ) {
21                 trace(oauthPolicy);
22                 Security.loadPolicyFile(oauthPolicy);
23             }
24                 }
25                 
26                 override public function loadBbox(left:Number,right:Number,
27                                                                 top:Number,bottom:Number):void {
28             var mapVars:URLVariables = new URLVariables();
29             mapVars.bbox= left+","+bottom+","+right+","+top;
30
31             var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"map");
32             mapRequest.data = mapVars;
33
34             var mapLoader:URLLoader = new URLLoader();
35             mapLoader.addEventListener(Event.COMPLETE, loadedMap);
36             mapLoader.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad);
37             mapLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
38             mapLoader.load(mapRequest);
39             dispatchEvent(new Event(LOAD_STARTED));
40                 }
41
42         private function parseTags(tagElements:XMLList):Object {
43             var tags:Object = {};
44             for each (var tagEl:XML in tagElements)
45                 tags[tagEl.@k] = tagEl.@v;
46             return tags;
47         }
48
49         private function errorOnMapLoad(event:Event):void {
50             trace("error loading map");
51         }
52         private function mapLoadStatus(event:HTTPStatusEvent):void {
53             trace("loading map status = "+event.status);
54         }
55         
56         private function loadedMap(event:Event):void {
57             dispatchEvent(new Event(LOAD_COMPLETED));
58
59             var map:XML = new XML(URLLoader(event.target).data);
60             var id:Number;
61             var version:uint;
62             var tags:Object;
63
64             for each(var nodeData:XML in map.node) {
65                 id = Number(nodeData.@id);
66                 version = uint(nodeData.@version);
67
68                 var node:Node = getNode(id);
69                 if ( node == null || !node.loaded ) {
70                     var lat:Number = Number(nodeData.@lat);
71                     var lon:Number = Number(nodeData.@lon);
72                     tags = parseTags(nodeData.tag);
73                     if ( node == null )
74                         setNode(new Node(id, version, tags, true, lat, lon),false);
75                     else {
76                         node.update(version, tags, true, lat, lon);
77                         sendEvent(new EntityEvent(NEW_NODE, node), false);
78                     }
79                 }
80             }
81
82             for each(var data:XML in map.way) {
83                 id = Number(data.@id);
84                 version = uint(data.@version);
85
86                 var way:Way = getWay(id);
87                 if ( way == null || !way.loaded ) {
88                     var nodes:Array = [];
89                     for each(var nd:XML in data.nd)
90                         nodes.push(getNode(Number(nd.@ref)));
91                     tags = parseTags(data.tag);
92                     if ( way == null )
93                         setWay(new Way(id, version, tags, true, nodes),false);
94                     else {
95                         way.update(version, tags, true, nodes);
96                         sendEvent(new EntityEvent(NEW_WAY, way), false);
97                     }
98                 }
99             }
100             
101             for each(var relData:XML in map.relation) {
102                 id = Number(relData.@id);
103                 version = uint(relData.@version);
104                 
105                 var rel:Relation = getRelation(id);
106                 if ( rel == null || !rel.loaded ) {
107                     tags = parseTags(relData.tag);
108                     var members:Array = [];
109                     for each(var memberXML:XML in relData.member) {
110                         var type:String = memberXML.@type.toLowerCase();
111                         var role:String = memberXML.@role;
112                         var memberID:Number = Number(memberXML.@ref);
113                         var member:Entity = null;
114                         if ( type == "node" ) {
115                             member = getNode(memberID);
116                             if ( member == null ) {
117                                 member = new Node(memberID,0,{},false,0,0);
118                                 setNode(Node(member),true);
119                             }
120                         } else if ( type == "way" ) {
121                             member = getWay(memberID);
122                             if (member == null) {
123                                 member = new Way(memberID,0,{},false,[]);
124                                 setWay(Way(member),true);
125                             }
126                         } else if ( type == "relation" ) {
127                             member = getRelation(memberID);
128                             if (member == null) {
129                                 member = new Relation(memberID,0,{},false,[]);
130                                 setRelation(Relation(member),true);
131                             }
132                         }
133                         
134                         if ( member != null )
135                             members.push(new RelationMember(member, role));
136                     }
137                     
138                     if ( rel == null )
139                         setRelation(new Relation(id, version, tags, true, members), false);
140                     else {
141                         rel.update(version,tags,true,members);
142                         sendEvent(new EntityEvent(NEW_RELATION, rel), false);
143                     }
144                 }
145             }
146             
147             registerPOINodes();
148         }
149         
150         protected function registerPOINodes():void {
151             for each (var nodeID:Number in getAllNodeIDs()) {
152                 var node:Node = getNode(nodeID);
153                 if (!node.hasParentWays)
154                     registerPOI(node);
155             }
156         }
157
158         protected var appID:OAuthConsumer;
159         protected var authToken:OAuthToken;
160         
161             override public function setAppID(id:Object):void {
162                 appID = OAuthConsumer(id);
163             }
164             
165             override public function setAuthToken(id:Object):void {
166                 authToken = OAuthToken(id);
167             }
168
169         private var httpStatus:int = 0;
170         
171         private function recordStatus(event:HTTPStatusEvent):void {
172             httpStatus = event.status;
173         }
174         
175         private var lastUploadedChangesetTags:Object;
176         
177         override public function createChangeset(tags:Object):void {
178             lastUploadedChangesetTags = tags;
179             
180                 var changesetXML:XML = <osm version="0.6"><changeset /></osm>;
181                 var changeset:XML = <changeset />;
182                 for (var tagKey:Object in tags) {
183               var tagXML:XML = <tag/>;
184               tagXML.@k = tagKey;
185               tagXML.@v = tags[tagKey];
186               changesetXML.changeset.appendChild(tagXML);
187             }        
188
189             // make an OAuth query
190             var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
191             var url:String = Connection.apiBaseURL+"changeset/create";
192             //var params:Object = { _method: "PUT" };
193             var oauthRequest:OAuthRequest = new OAuthRequest("PUT", url, null, appID, authToken);
194             var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
195
196             // build the actual request
197             var urlReq:URLRequest = new URLRequest(String(urlStr));
198             urlReq.method = "POST";
199             urlReq.data = changesetXML.toXMLString();
200             urlReq.contentType = "application/xml";
201             urlReq.requestHeaders = new Array(new URLRequestHeader("X_HTTP_METHOD_OVERRIDE", "PUT"));
202             var loader:URLLoader = new URLLoader();
203             loader.addEventListener(Event.COMPLETE, changesetCreateComplete);
204             loader.addEventListener(IOErrorEvent.IO_ERROR, changesetCreateError);
205             loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
206                 loader.load(urlReq);
207             }
208
209         private function changesetCreateComplete(event:Event):void {
210             // response should be a Number changeset id
211             var id:Number = Number(URLLoader(event.target).data);
212             
213             // which means we now have a new changeset!
214             setActiveChangeset(new Changeset(id, lastUploadedChangesetTags));
215         }
216
217         private function changesetCreateError(event:IOErrorEvent):void {
218             dispatchEvent(new Event(NEW_CHANGESET_ERROR));
219         }
220         
221         override public function uploadChanges():void {
222             var changeset:Changeset = getActiveChangeset();
223             var upload:XML = <osmChange version="0.6"/>
224             upload.appendChild(addCreated(changeset, getAllNodeIDs, getNode, serialiseNode));
225             upload.appendChild(addCreated(changeset, getAllWayIDs, getWay, serialiseWay));
226             upload.appendChild(addCreated(changeset, getAllRelationIDs, getRelation, serialiseRelation));
227             upload.appendChild(addModified(changeset, getAllNodeIDs, getNode, serialiseNode));
228             upload.appendChild(addModified(changeset, getAllWayIDs, getWay, serialiseWay));
229             upload.appendChild(addModified(changeset, getAllRelationIDs, getRelation, serialiseRelation));
230             upload.appendChild(addDeleted(changeset, getAllRelationIDs, getRelation, serialiseEntityRoot));
231             upload.appendChild(addDeleted(changeset, getAllWayIDs, getWay, serialiseEntityRoot));
232             upload.appendChild(addDeleted(changeset, getAllNodeIDs, getNode, serialiseEntityRoot));
233
234                         trace(upload);
235             
236             // now actually upload them
237             // make an OAuth query
238             var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
239             var url:String = Connection.apiBaseURL+"changeset/" + changeset.id + "/upload";
240             var oauthRequest:OAuthRequest = new OAuthRequest("POST", url, null, appID, authToken);
241             var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
242
243             // build the actual request
244             var urlReq:URLRequest = new URLRequest(String(urlStr));
245             urlReq.method = "POST";
246             urlReq.data = upload.toXMLString();
247             urlReq.contentType = "text/xml";
248             var loader:URLLoader = new URLLoader();
249             loader.addEventListener(Event.COMPLETE, diffUploadComplete);
250             loader.addEventListener(IOErrorEvent.IO_ERROR, diffUploadError);
251             loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
252                 loader.load(urlReq);
253                 
254                 dispatchEvent(new Event(SAVE_STARTED));
255         }
256
257         private function diffUploadComplete(event:Event):void {
258             // response should be XML describing the progress
259             var results:XML = new XML((URLLoader(event.target).data));
260             
261             for each( var update:XML in results.child("*") ) {
262                 var oldID:Number = Number(update.@old_id);
263                 var newID:Number = Number(update.@new_id);
264                 var version:uint = uint(update.@new_version);
265                 var type:String = update.name();
266
267                                 if (newID==0) {
268                                         // delete
269                         if ( type == "node" ) killNode(oldID);
270                         else if ( type == "way" ) killWay(oldID);
271                         else if ( type == "relation" ) killRelation(oldID);
272                                         
273                                 } else {
274                                         // create/update
275                         var entity:Entity;
276                         if ( type == "node" ) entity = getNode(oldID);
277                         else if ( type == "way" ) entity = getWay(oldID);
278                         else if ( type == "relation" ) entity = getRelation(oldID);
279                         entity.markClean(newID, version);
280                 
281                         if ( oldID != newID ) {
282                             if ( type == "node" ) renumberNode(oldID, entity as Node, false);
283                             else if ( type == "way" ) renumberWay(oldID, entity as Way, false);
284                             else if ( type == "relation" ) renumberRelation(oldID, entity as Relation, false);
285                         }
286                                 }
287             }
288
289                 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, true));
290         }
291
292         private function diffUploadError(event:IOErrorEvent):void {
293             trace("error "+URLLoader(event.target).data + " "+httpStatus+ " " + event.text);
294
295                 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, false));
296         }
297
298         private function addCreated(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
299             var create:XML = <create version="0.6"/>
300             for each( var id:Number in getIDs() ) {
301                 var entity:Entity = get(id);
302                 if ( id >= 0 || entity.deleted )
303                     continue;
304                     
305                 var xml:XML = serialise(entity);
306                 xml.@changeset = changeset.id;
307                 create.appendChild(xml);
308             }
309             return create.hasComplexContent() ? create : <!-- blank create section -->;
310         }
311
312                 private function addDeleted(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
313             var del:XML = <delete version="0.6"/>
314             for each( var id:Number in getIDs() ) {
315                 var entity:Entity = get(id);
316                 // creates are already included
317                 if ( id < 0 || !entity.deleted )
318                     continue;
319                     
320                 var xml:XML = serialise(entity);
321                 xml.@changeset = changeset.id;
322                 del.appendChild(xml);
323             }
324             return del.hasComplexContent() ? del : <!-- blank delete section -->;
325                 }
326
327         private function addModified(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
328             var modify:XML = <modify version="0.6"/>
329             for each( var id:Number in getIDs() ) {
330                 var entity:Entity = get(id);
331                 // creates and deletes are already included
332                 if ( id < 0 || entity.deleted || !entity.isDirty )
333                     continue;
334                     
335                 var xml:XML = serialise(entity);
336                 xml.@changeset = changeset.id;
337                 modify.appendChild(xml);
338             }
339             return modify.hasComplexContent() ? modify : <!-- blank modify section -->;
340         }
341
342         private function serialiseNode(node:Node):XML {
343             var xml:XML = serialiseEntityRoot(node); //<node/>
344             serialiseEntityTags(node, xml);
345             xml.@lat = node.lat;
346             xml.@lon = node.lon;
347             return xml;
348         }
349
350         private function serialiseWay(way:Way):XML {
351             var xml:XML = serialiseEntityRoot(way); //<node/>
352             serialiseEntityTags(way, xml);
353             for ( var i:uint = 0; i < way.length; i++ ) {
354                 var nd:XML = <nd/>
355                 nd.@ref = way.getNode(i).id;
356                 xml.appendChild(nd);
357             }
358             return xml;
359         }
360
361         private function serialiseRelation(relation:Relation):XML {
362             var xml:XML = serialiseEntityRoot(Relation); //<node/>
363             serialiseEntityTags(relation, xml);
364             for ( var i:uint = 0; i < relation.length; i++ ) {
365                 var relMember:RelationMember = relation.getMember(i);
366                 var member:XML = <member/>
367                 member.@ref = relMember.entity.id;
368                 member.@type = relMember.entity.getType();
369                 member.@role = relMember.role;
370                 xml.appendChild(member);
371             }
372             return xml;
373         }
374         
375                 private function serialiseEntityRoot(entity:Object):XML {
376                         var xml:XML;
377                         if      (entity is Way     ) { xml = <way/> }
378                         else if (entity is Node    ) { xml = <node/> }
379                         else if (entity is Relation) { xml = <relation/> }
380                         xml.@id = entity.id;
381                         xml.@version = entity.version;
382                         return xml;
383                 }
384
385         private function serialiseEntityTags(entity:Entity, xml:XML):void {
386             xml.@id = entity.id;
387             xml.@version = entity.version;
388             for each( var tag:Tag in entity.getTagArray() ) {
389               var tagXML:XML = <tag/>
390               tagXML.@k = tag.key;
391               tagXML.@v = tag.value;
392               xml.appendChild(tagXML);
393             }
394         }
395
396         }
397 }