package net.systemeD.halcyon.connection { import flash.events.*; import flash.system.Security; import flash.net.*; import org.iotashan.oauth.*; public class XMLConnection extends Connection { //public var readConnection:NetConnection; public function XMLConnection() { if (Connection.policyURL!='') Security.loadPolicyFile(Connection.policyURL); var oauthPolicy:String = Connection.getParam("oauth_policy", ""); if ( oauthPolicy != "" ) { trace(oauthPolicy); Security.loadPolicyFile(oauthPolicy); } } override public function loadBbox(left:Number,right:Number, top:Number,bottom:Number):void { var mapVars:URLVariables = new URLVariables(); mapVars.bbox= left+","+bottom+","+right+","+top; var mapRequest:URLRequest = new URLRequest(Connection.apiBaseURL+"map"); mapRequest.data = mapVars; var mapLoader:URLLoader = new URLLoader(); mapLoader.addEventListener(Event.COMPLETE, loadedMap); mapLoader.load(mapRequest); dispatchEvent(new Event(LOAD_STARTED)); } private function parseTags(tagElements:XMLList):Object { var tags:Object = {}; for each (var tagEl:XML in tagElements) tags[tagEl.@k] = tagEl.@v; return tags; } private function loadedMap(event:Event):void { dispatchEvent(new Event(LOAD_COMPLETED)); var map:XML = new XML(URLLoader(event.target).data); var id:Number; var version:uint; var tags:Object; for each(var nodeData:XML in map.node) { id = Number(nodeData.@id); version = uint(nodeData.@version); var node:Node = getNode(id); if ( node == null ) { var lat:Number = Number(nodeData.@lat); var lon:Number = Number(nodeData.@lon); tags = parseTags(nodeData.tag); setNode(new Node(id, version, tags, true, lat, lon)); } } for each(var data:XML in map.way) { id = Number(data.@id); version = uint(data.@version); var way:Way = getWay(id); if ( way == null ) { var nodes:Array = []; for each(var nd:XML in data.nd) nodes.push(getNode(Number(nd.@ref))); tags = parseTags(data.tag); setWay(new Way(id, version, tags,true, nodes)); } } registerPOINodes(); } protected function registerPOINodes():void { for each (var nodeID:Number in getAllNodeIDs()) { var node:Node = getNode(nodeID); if ( node.parentWays.length == 0 ) registerPOI(node); } } protected var appID:OAuthConsumer; protected var authToken:OAuthToken; override public function setAppID(id:Object):void { appID = OAuthConsumer(id); } override public function setAuthToken(id:Object):void { authToken = OAuthToken(id); } private var httpStatus:int = 0; private function recordStatus(event:HTTPStatusEvent):void { httpStatus = event.status; } private var lastUploadedChangesetTags:Object; override public function createChangeset(tags:Object):void { lastUploadedChangesetTags = tags; var changesetXML:XML = ; var changeset:XML = ; for (var tagKey:Object in tags) { var tagXML:XML = ; tagXML.@k = tagKey; tagXML.@v = tags[tagKey]; changesetXML.changeset.appendChild(tagXML); } // make an OAuth query var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1(); var url:String = Connection.apiBaseURL+"changeset/create"; //var params:Object = { _method: "PUT" }; var oauthRequest:OAuthRequest = new OAuthRequest("PUT", url, null, appID, authToken); var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING) // build the actual request var urlReq:URLRequest = new URLRequest(String(urlStr)); urlReq.method = "POST"; urlReq.data = changesetXML.toXMLString(); urlReq.contentType = "application/xml"; urlReq.requestHeaders = new Array(new URLRequestHeader("X_HTTP_METHOD_OVERRIDE", "PUT")); var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, changesetCreateComplete); loader.addEventListener(IOErrorEvent.IO_ERROR, changesetCreateError); loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus); loader.load(urlReq); } private function changesetCreateComplete(event:Event):void { // response should be a Number changeset id var id:Number = Number(URLLoader(event.target).data); // which means we now have a new changeset! setActiveChangeset(new Changeset(id, lastUploadedChangesetTags)); } private function changesetCreateError(event:IOErrorEvent):void { dispatchEvent(new Event(NEW_CHANGESET_ERROR)); } override public function uploadChanges():void { var changeset:Changeset = getActiveChangeset(); var upload:XML = upload.appendChild(addCreated(changeset, getAllNodeIDs, getNode, serialiseNode)); upload.appendChild(addCreated(changeset, getAllWayIDs, getWay, serialiseWay)); upload.appendChild(addCreated(changeset, getAllRelationIDs, getRelation, serialiseRelation)); upload.appendChild(addModified(changeset, getAllNodeIDs, getNode, serialiseNode)); upload.appendChild(addModified(changeset, getAllWayIDs, getWay, serialiseWay)); upload.appendChild(addModified(changeset, getAllRelationIDs, getRelation, serialiseRelation)); // *** TODO *** deleting items // now actually upload them // make an OAuth query var sig:IOAuthSignatureMethod = new OAuthSignatureMethod_HMAC_SHA1(); var url:String = Connection.apiBaseURL+"changeset/" + changeset.id + "/upload"; var oauthRequest:OAuthRequest = new OAuthRequest("POST", url, null, appID, authToken); var urlStr:Object = oauthRequest.buildRequest(sig, OAuthRequest.RESULT_TYPE_URL_STRING) // build the actual request var urlReq:URLRequest = new URLRequest(String(urlStr)); urlReq.method = "POST"; urlReq.data = upload.toXMLString(); urlReq.contentType = "text/xml"; var loader:URLLoader = new URLLoader(); loader.addEventListener(Event.COMPLETE, diffUploadComplete); loader.addEventListener(IOErrorEvent.IO_ERROR, diffUploadError); loader.addEventListener(HTTPStatusEvent.HTTP_STATUS, recordStatus); loader.load(urlReq); dispatchEvent(new Event(SAVE_STARTED)); } private function diffUploadComplete(event:Event):void { // response should be XML describing the progress var results:XML = new XML((URLLoader(event.target).data)); for each( var update:XML in results.child("*") ) { var oldID:Number = Number(update.@old_id); var newID:Number = Number(update.@new_id); var version:uint = uint(update.@new_version); var type:String = update.name(); var entity:Entity; if ( type == "node" ) entity = getNode(oldID); else if ( type == "way" ) entity = getWay(oldID); else if ( type == "relation" ) entity = getRelation(oldID); entity.markClean(newID, version); if ( oldID != newID ) { if ( type == "node" ) renumberNode(oldID, entity as Node); else if ( type == "way" ) renumberWay(oldID, entity as Way); else if ( type == "relation" ) renumberRelation(oldID, entity as Relation); } // *** TODO *** handle deleting } dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, true)); } private function diffUploadError(event:IOErrorEvent):void { trace("error "+URLLoader(event.target).data + " "+httpStatus+ " " + event.text); dispatchEvent(new SaveCompleteEvent(SAVE_COMPLETED, false)); } private function addCreated(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML { var create:XML = for each( var id:Number in getIDs() ) { if ( id >= 0 ) continue; var entity:Object = get(id); var xml:XML = serialise(entity); xml.@changeset = changeset.id; create.appendChild(xml); } return create.hasComplexContent() ? create : ; } private function addModified(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML { var modify:XML = for each( var id:Number in getIDs() ) { var entity:Entity = get(id); // creates are already included if ( id < 0 || !entity.isDirty ) continue; var xml:XML = serialise(entity); xml.@changeset = changeset.id; modify.appendChild(xml); } return modify.hasComplexContent() ? modify : ; } private function serialiseNode(node:Node):XML { var xml:XML = serialiseEntity(node, xml); xml.@lat = node.lat; xml.@lon = node.lon; return xml; } private function serialiseWay(way:Way):XML { var xml:XML = serialiseEntity(way, xml); for ( var i:uint = 0; i < way.length; i++ ) { var nd:XML = nd.@ref = way.getNode(i).id; xml.appendChild(nd); } return xml; } private function serialiseRelation(relation:Relation):XML { var xml:XML = serialiseEntity(relation, xml); for ( var i:uint = 0; i < relation.length; i++ ) { var relMember:RelationMember = relation.getMember(i); var member:XML = member.@ref = relMember.entity.id; member.@type = relMember.entity.getType(); member.@role = relMember.role; xml.appendChild(member); } return xml; } private function serialiseEntity(entity:Entity, xml:XML):void { xml.@id = entity.id; xml.@version = entity.version; for each( var tag:Tag in entity.getTagArray() ) { var tagXML:XML = tagXML.@k = tag.key; tagXML.@v = tag.value; xml.appendChild(tagXML); } } } }