1 package net.systemeD.halcyon.connection {
5 import flash.system.Security;
7 import org.iotashan.oauth.*;
8 import mx.controls.Alert;
10 import net.systemeD.halcyon.Globals;
12 public class XMLConnection extends XMLBaseConnection {
14 //public var readConnection:NetConnection;
16 public function XMLConnection() {
18 if (Connection.policyURL!='')
19 Security.loadPolicyFile(Connection.policyURL);
20 var oauthPolicy:String = Connection.getParam("oauth_policy", "");
21 if ( oauthPolicy != "" ) {
22 Security.loadPolicyFile(oauthPolicy);
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;
31 var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"map");
32 mapRequest.data = mapVars;
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));
42 private function errorOnMapLoad(event:Event):void {
43 Alert.show("Couldn't load the map", 'Error', mx.controls.Alert.OK);
45 private function mapLoadStatus(event:HTTPStatusEvent):void {
46 trace("loading map status = "+event.status);
49 protected var appID:OAuthConsumer;
50 protected var authToken:OAuthToken;
52 override public function setAppID(id:Object):void {
53 appID = OAuthConsumer(id);
56 override public function setAuthToken(id:Object):void {
57 authToken = OAuthToken(id);
60 private var httpStatus:int = 0;
62 private function recordStatus(event:HTTPStatusEvent):void {
63 httpStatus = event.status;
66 private var lastUploadedChangesetTags:Object;
68 override public function createChangeset(tags:Object):void {
69 lastUploadedChangesetTags = tags;
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/>;
76 tagXML.@v = tags[tagKey];
77 changesetXML.changeset.appendChild(tagXML);
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)
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);
100 private function changesetCreateComplete(event:Event):void {
101 // response should be a Number changeset id
102 var id:Number = Number(URLLoader(event.target).data);
104 // which means we now have a new changeset!
105 setActiveChangeset(new Changeset(id, lastUploadedChangesetTags));
108 private function changesetCreateError(event:IOErrorEvent):void {
109 dispatchEvent(new Event(NEW_CHANGESET_ERROR));
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));
125 // now actually upload them
126 // make an OAuth query
127 var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1();
128 var url:String = Connection.apiBaseURL+"changeset/" + changeset.id + "/upload";
129 var oauthRequest:OAuthRequest = new OAuthRequest("POST", url, null, appID, authToken);
130 var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING)
132 // build the actual request
133 var urlReq:URLRequest = new URLRequest(String(urlStr));
134 urlReq.method = "POST";
135 urlReq.data = upload.toXMLString();
136 urlReq.contentType = "text/xml";
137 var loader:URLLoader = new URLLoader();
138 loader.addEventListener(Event.COMPLETE, diffUploadComplete);
139 loader.addEventListener(IOErrorEvent.IO_ERROR, diffUploadError);
140 loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus);
143 dispatchEvent(new Event(SAVE_STARTED));
146 private function diffUploadComplete(event:Event):void {
147 // response should be XML describing the progress
148 var results:XML = new XML((URLLoader(event.target).data));
150 for each( var update:XML in results.child("*") ) {
151 var oldID:Number = Number(update.@old_id);
152 var newID:Number = Number(update.@new_id);
153 var version:uint = uint(update.@new_version);
154 var type:String = update.name();
158 if ( type == "node" ) killNode(oldID);
159 else if ( type == "way" ) killWay(oldID);
160 else if ( type == "relation" ) killRelation(oldID);
165 if ( type == "node" ) entity = getNode(oldID);
166 else if ( type == "way" ) entity = getWay(oldID);
167 else if ( type == "relation" ) entity = getRelation(oldID);
168 entity.markClean(newID, version);
170 if ( oldID != newID ) {
171 if ( type == "node" ) renumberNode(oldID, entity as Node, false);
172 else if ( type == "way" ) renumberWay(oldID, entity as Way, false);
173 else if ( type == "relation" ) renumberRelation(oldID, entity as Relation, false);
178 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, true));
179 markClean(); // marks the connection clean. Pressing undo from this point on leads to unexpected results
180 MainUndoStack.getGlobalStack().breakUndo(); // so, for now, break the undo stack
183 private function diffUploadError(event:IOErrorEvent):void {
184 Alert.show("Couldn't upload data: "+httpStatus+" "+event.text, 'Error', mx.controls.Alert.OK);
185 dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, false));
188 private function addCreated(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
189 var create:XML = <create version="0.6"/>
190 for each( var id:Number in getIDs() ) {
191 var entity:Entity = get(id);
192 if ( id >= 0 || entity.deleted )
195 var xml:XML = serialise(entity);
196 xml.@changeset = changeset.id;
197 create.appendChild(xml);
199 return create.hasComplexContent() ? create : <!-- blank create section -->;
202 private function addDeleted(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
203 var del:XML = <delete version="0.6"/>
204 for each( var id:Number in getIDs() ) {
205 var entity:Entity = get(id);
206 // creates are already included
207 if ( id < 0 || !entity.deleted )
210 var xml:XML = serialise(entity);
211 xml.@changeset = changeset.id;
212 del.appendChild(xml);
214 return del.hasComplexContent() ? del : <!-- blank delete section -->;
217 private function addModified(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
218 var modify:XML = <modify version="0.6"/>
219 for each( var id:Number in getIDs() ) {
220 var entity:Entity = get(id);
221 // creates and deletes are already included
222 if ( id < 0 || entity.deleted || !entity.isDirty )
225 var xml:XML = serialise(entity);
226 xml.@changeset = changeset.id;
227 modify.appendChild(xml);
229 return modify.hasComplexContent() ? modify : <!-- blank modify section -->;
232 private function serialiseNode(node:Node):XML {
233 var xml:XML = serialiseEntityRoot(node); //<node/>
234 serialiseEntityTags(node, xml);
240 private function serialiseWay(way:Way):XML {
241 var xml:XML = serialiseEntityRoot(way); //<node/>
242 serialiseEntityTags(way, xml);
243 for ( var i:uint = 0; i < way.length; i++ ) {
245 nd.@ref = way.getNode(i).id;
251 private function serialiseRelation(relation:Relation):XML {
252 var xml:XML = serialiseEntityRoot(relation); //<node/>
253 serialiseEntityTags(relation, xml);
254 for ( var i:uint = 0; i < relation.length; i++ ) {
255 var relMember:RelationMember = relation.getMember(i);
256 var member:XML = <member/>
257 member.@ref = relMember.entity.id;
258 member.@type = relMember.entity.getType();
259 member.@role = relMember.role;
260 xml.appendChild(member);
265 private function serialiseEntityRoot(entity:Object):XML {
267 if (entity is Way ) { xml = <way/> }
268 else if (entity is Node ) { xml = <node/> }
269 else if (entity is Relation) { xml = <relation/> }
271 xml.@version = entity.version;
275 private function serialiseEntityTags(entity:Entity, xml:XML):void {
277 xml.@version = entity.version;
278 for each( var tag:Tag in entity.getTagArray() ) {
279 var tagXML:XML = <tag/>
281 tagXML.@v = tag.value;
282 xml.appendChild(tagXML);