create new ways w00t! and extend old ones
[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.load(mapRequest);
36             dispatchEvent(new Event(LOAD_STARTED));
37                 }
38
39         private function parseTags(tagElements:XMLList):Object {
40             var tags:Object = {};
41             for each (var tagEl:XML in tagElements)
42                 tags[tagEl.@k] = tagEl.@v;
43             return tags;
44         }
45
46         private function loadedMap(event:Event):void {
47             dispatchEvent(new Event(LOAD_COMPLETED));
48
49             var map:XML = new XML(URLLoader(event.target).data);
50             var id:Number;
51             var version:uint;
52             var tags:Object;
53
54             for each(var nodeData:XML in map.node) {
55                 id = Number(nodeData.@id);
56                 version = uint(nodeData.@version);
57
58                 var node:Node = getNode(id);
59                 if ( node == null ) {
60                     var lat:Number = Number(nodeData.@lat);
61                     var lon:Number = Number(nodeData.@lon);
62                     tags = parseTags(nodeData.tag);
63                     setNode(new Node(id, version, tags, true, lat, lon));
64                 }
65             }
66
67             for each(var data:XML in map.way) {
68                 id = Number(data.@id);
69                 version = uint(data.@version);
70
71                 var way:Way = getWay(id);
72                 if ( way == null ) {
73                     var nodes:Array = [];
74                     for each(var nd:XML in data.nd)
75                         nodes.push(getNode(Number(nd.@ref)));
76                     tags = parseTags(data.tag);
77                     setWay(new Way(id, version, tags,true,  nodes));
78                 }
79             }
80             
81             registerPOINodes();
82         }
83         
84         protected function registerPOINodes():void {
85             for each (var nodeID:Number in getAllNodeIDs()) {
86                 var node:Node = getNode(nodeID);
87                 if ( node.parentWays.length == 0 )
88                     registerPOI(node);
89             }
90         }
91
92         protected var appID:OAuthConsumer;
93         protected var authToken:OAuthToken;
94         
95             override public function setAppID(id:Object):void {
96                 appID = OAuthConsumer(id);
97             }
98             
99             override public function setAuthToken(id:Object):void {
100                 authToken = OAuthToken(id);
101             }
102
103         private var httpStatus:int = 0;
104         
105         private function recordStatus(event:HTTPStatusEvent):void {
106             httpStatus = event.status;
107         }
108         
109         private var lastUploadedChangesetTags:Object;
110         
111         override public function createChangeset(tags:Object):void {
112             lastUploadedChangesetTags = tags;
113             
114                 var changesetXML:XML = <osm version="0.6"><changeset /></osm>;
115                 var changeset:XML = <changeset />;
116                 for (var tagKey:Object in tags) {
117               var tagXML:XML = <tag/>;
118               tagXML.@k = tagKey;
119               tagXML.@v = tags[tagKey];
120               changesetXML.changeset.appendChild(tagXML);
121             }        
122
123             // make an OAuth query
124             var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
125             var url:String = Connection.apiBaseURL+"changeset/create";
126             //var params:Object = { _method: "PUT" };
127             var oauthRequest:OAuthRequest = new OAuthRequest("PUT", url, null, appID, authToken);
128             var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
129
130             // build the actual request
131             var urlReq:URLRequest = new URLRequest(String(urlStr));
132             urlReq.method = "POST";
133             urlReq.data = changesetXML.toXMLString();
134             urlReq.contentType = "application/xml";
135             urlReq.requestHeaders = new Array(new URLRequestHeader("X_HTTP_METHOD_OVERRIDE", "PUT"));
136             var loader:URLLoader = new URLLoader();
137             loader.addEventListener(Event.COMPLETE, changesetCreateComplete);
138             loader.addEventListener(IOErrorEvent.IO_ERROR, changesetCreateError);
139             loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
140                 loader.load(urlReq);
141             }
142
143         private function changesetCreateComplete(event:Event):void {
144             // response should be a Number changeset id
145             var id:Number = Number(URLLoader(event.target).data);
146             
147             // which means we now have a new changeset!
148             setActiveChangeset(new Changeset(id, lastUploadedChangesetTags));
149         }
150
151         private function changesetCreateError(event:IOErrorEvent):void {
152             dispatchEvent(new Event(NEW_CHANGESET_ERROR));
153         }
154         
155         override public function uploadChanges():void {
156             var changeset:Changeset = getActiveChangeset();
157             var upload:XML = <osmChange version="0.6"/>
158             upload.appendChild(addCreated(changeset, getAllNodeIDs, getNode, serialiseNode));
159             upload.appendChild(addCreated(changeset, getAllWayIDs, getWay, serialiseWay));
160             upload.appendChild(addCreated(changeset, getAllRelationIDs, getRelation, serialiseRelation));
161             upload.appendChild(addModified(changeset, getAllNodeIDs, getNode, serialiseNode));
162             upload.appendChild(addModified(changeset, getAllWayIDs, getWay, serialiseWay));
163             upload.appendChild(addModified(changeset, getAllRelationIDs, getRelation, serialiseRelation));
164
165             // *** TODO *** deleting items
166             
167             // now actually upload them
168             // make an OAuth query
169             var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
170             var url:String = Connection.apiBaseURL+"changeset/" + changeset.id + "/upload";
171             var oauthRequest:OAuthRequest = new OAuthRequest("POST", url, null, appID, authToken);
172             var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
173
174             // build the actual request
175             var urlReq:URLRequest = new URLRequest(String(urlStr));
176             urlReq.method = "POST";
177             urlReq.data = upload.toXMLString();
178             urlReq.contentType = "text/xml";
179             var loader:URLLoader = new URLLoader();
180             loader.addEventListener(Event.COMPLETE, diffUploadComplete);
181             loader.addEventListener(IOErrorEvent.IO_ERROR, diffUploadError);
182             loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
183                 loader.load(urlReq);
184                 
185                 dispatchEvent(new Event(SAVE_STARTED));
186         }
187
188         private function diffUploadComplete(event:Event):void {
189             // response should be XML describing the progress
190             var results:XML = new XML((URLLoader(event.target).data));
191             
192             for each( var update:XML in results.child("*") ) {
193                 var oldID:Number = Number(update.@old_id);
194                 var newID:Number = Number(update.@new_id);
195                 var version:uint = uint(update.@new_version);
196                 var type:String = update.name();
197                 
198                 var entity:Entity;
199                 if ( type == "node" ) entity = getNode(oldID);
200                 else if ( type == "way" ) entity = getWay(oldID);
201                 else if ( type == "relation" ) entity = getRelation(oldID);
202                 entity.markClean(newID, version);
203                 
204                 if ( oldID != newID ) {
205                     if ( type == "node" ) renumberNode(oldID, entity as Node);
206                     else if ( type == "way" ) renumberWay(oldID, entity as Way);
207                     else if ( type == "relation" ) renumberRelation(oldID, entity as Relation);
208                 }
209                 // *** TODO *** handle deleting
210             }
211
212                 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, true));
213         }
214
215         private function diffUploadError(event:IOErrorEvent):void {
216             trace("error "+URLLoader(event.target).data + " "+httpStatus+ " " + event.text);
217
218                 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, false));
219         }
220
221         private function addCreated(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
222             var create:XML = <create version="0.6"/>
223             for each( var id:Number in getIDs() ) {
224                 if ( id >= 0 )
225                     continue;
226                     
227                 var entity:Object = get(id);
228                 var xml:XML = serialise(entity);
229                 xml.@changeset = changeset.id;
230                 create.appendChild(xml);
231             }
232             return create.hasComplexContent() ? create : <!-- blank create section -->;
233         }
234
235         private function addModified(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
236             var modify:XML = <modify version="0.6"/>
237             for each( var id:Number in getIDs() ) {
238                 var entity:Entity = get(id);
239                 // creates are already included
240                 if ( id < 0 || !entity.isDirty )
241                     continue;
242                     
243                 var xml:XML = serialise(entity);
244                 xml.@changeset = changeset.id;
245                 modify.appendChild(xml);
246             }
247             return modify.hasComplexContent() ? modify : <!-- blank modify section -->;
248         }
249
250         private function serialiseNode(node:Node):XML {
251             var xml:XML = <node/>
252             serialiseEntity(node, xml);
253             xml.@lat = node.lat;
254             xml.@lon = node.lon;
255             return xml;
256         }
257
258         private function serialiseWay(way:Way):XML {
259             var xml:XML = <way/>
260             serialiseEntity(way, xml);
261             for ( var i:uint = 0; i < way.length; i++ ) {
262                 var nd:XML = <nd/>
263                 nd.@ref = way.getNode(i).id;
264                 xml.appendChild(nd);
265             }
266             return xml;
267         }
268
269         private function serialiseRelation(relation:Relation):XML {
270             var xml:XML = <relation/>
271             serialiseEntity(relation, xml);
272             for ( var i:uint = 0; i < relation.length; i++ ) {
273                 var relMember:RelationMember = relation.getMember(i);
274                 var member:XML = <member/>
275                 member.@ref = relMember.entity.id;
276                 member.@type = relMember.entity.getType();
277                 member.@role = relMember.role;
278                 xml.appendChild(member);
279             }
280             return xml;
281         }
282         
283         private function serialiseEntity(entity:Entity, xml:XML):void {
284             xml.@id = entity.id;
285             xml.@version = entity.version;
286             for each( var tag:Tag in entity.getTagArray() ) {
287               var tagXML:XML = <tag/>
288               tagXML.@k = tag.key;
289               tagXML.@v = tag.value;
290               xml.appendChild(tagXML);
291             }
292         }
293         }
294 }