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