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