package net.systemeD.halcyon.connection {
import flash.events.*;
import flash.system.Security;
import flash.net.*;
import org.iotashan.oauth.*;
import net.systemeD.halcyon.Globals;
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.addEventListener(IOErrorEvent.IO_ERROR, errorOnMapLoad);
mapLoader.addEventListener(HTTPStatusEvent.HTTP_STATUS, mapLoadStatus);
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 errorOnMapLoad(event:Event):void {
trace("error loading map");
}
private function mapLoadStatus(event:HTTPStatusEvent):void {
trace("loading map status = "+event.status);
}
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 || !node.loaded ) {
var lat:Number = Number(nodeData.@lat);
var lon:Number = Number(nodeData.@lon);
tags = parseTags(nodeData.tag);
if ( node == null )
setNode(new Node(id, version, tags, true, lat, lon),false);
else {
node.update(version, tags, true, lat, lon);
sendEvent(new EntityEvent(NEW_NODE, node), false);
}
}
}
for each(var data:XML in map.way) {
id = Number(data.@id);
version = uint(data.@version);
var way:Way = getWay(id);
if ( way == null || !way.loaded ) {
var nodes:Array = [];
for each(var nd:XML in data.nd)
nodes.push(getNode(Number(nd.@ref)));
tags = parseTags(data.tag);
if ( way == null )
setWay(new Way(id, version, tags, true, nodes),false);
else {
way.update(version, tags, true, nodes);
sendEvent(new EntityEvent(NEW_WAY, way), false);
}
}
}
for each(var relData:XML in map.relation) {
id = Number(relData.@id);
version = uint(relData.@version);
var rel:Relation = getRelation(id);
if ( rel == null || !rel.loaded ) {
tags = parseTags(relData.tag);
var members:Array = [];
for each(var memberXML:XML in relData.member) {
var type:String = memberXML.@type.toLowerCase();
var role:String = memberXML.@role;
var memberID:Number = Number(memberXML.@ref);
var member:Entity = null;
if ( type == "node" ) {
member = getNode(memberID);
if ( member == null ) {
member = new Node(memberID,0,{},false,0,0);
setNode(Node(member),true);
}
} else if ( type == "way" ) {
member = getWay(memberID);
if (member == null) {
member = new Way(memberID,0,{},false,[]);
setWay(Way(member),true);
}
} else if ( type == "relation" ) {
member = getRelation(memberID);
if (member == null) {
member = new Relation(memberID,0,{},false,[]);
setRelation(Relation(member),true);
}
}
if ( member != null )
members.push(new RelationMember(member, role));
}
if ( rel == null )
setRelation(new Relation(id, version, tags, true, members), false);
else {
rel.update(version,tags,true,members);
sendEvent(new EntityEvent(NEW_RELATION, rel), false);
}
}
}
registerPOINodes();
}
protected function registerPOINodes():void {
for each (var nodeID:Number in getAllNodeIDs()) {
var node:Node = getNode(nodeID);
if (!node.hasParentWays)
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(addDeleted(changeset, getAllRelationIDs, getRelation, serialiseEntityRoot));
upload.appendChild(addDeleted(changeset, getAllWayIDs, getWay, serialiseEntityRoot));
upload.appendChild(addDeleted(changeset, getAllNodeIDs, getNode, serialiseEntityRoot));
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();
if (newID==0) {
// delete
if ( type == "node" ) killNode(oldID);
else if ( type == "way" ) killWay(oldID);
else if ( type == "relation" ) killRelation(oldID);
} else {
// create/update
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, false);
else if ( type == "way" ) renumberWay(oldID, entity as Way, false);
else if ( type == "relation" ) renumberRelation(oldID, entity as Relation, false);
}
}
}
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 addDeleted(changeset:Changeset, getIDs:Function, get:Function, serialise:Function):XML {
var del:XML =
for each( var id:Number in getIDs() ) {
var entity:Entity = get(id);
// creates are already included
if ( id < 0 || !entity.deleted )
continue;
var xml:XML = serialise(entity);
xml.@changeset = changeset.id;
del.appendChild(xml);
}
return del.hasComplexContent() ? del : ;
}
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 and deletes are already included
if ( id < 0 || entity.deleted || !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 = serialiseEntityRoot(node); //
serialiseEntityTags(node, xml);
xml.@lat = node.lat;
xml.@lon = node.lon;
return xml;
}
private function serialiseWay(way:Way):XML {
var xml:XML = serialiseEntityRoot(way); //
serialiseEntityTags(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 = serialiseEntityRoot(Relation); //
serialiseEntityTags(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 serialiseEntityRoot(entity:Object):XML {
var xml:XML;
if (entity is Way ) { xml = }
else if (entity is Node ) { xml = }
else if (entity is Relation) { xml = }
xml.@id = entity.id;
xml.@version = entity.version;
return xml;
}
private function serialiseEntityTags(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);
}
}
}
}