package data
{
import events.LoginEvent;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.net.URLLoader;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import mx.collections.IViewCursor;
import mx.collections.XMLListCollection;
import mx.controls.Alert;
import mx.events.CollectionEvent;
import mx.events.PropertyChangeEvent;
[Event(name="login", type="events.LoginEvent")]
public class AbstractDataSource extends EventDispatcher
{
private var _hasChanges:Boolean;
private var _login:String;
private var configUrl:String = "config.xml";
private var config:XML;
private var propertiesUrl:String = "properties.xml";
public var properties:XML;
private var defaultConfigUrl:String = "defaultGUIConfig.xml";
public var defaultConfig:XML;
public var entities:Array;
public var trees:Array;
private var _proxyServerUrl:String;
private var lastClientId:int = 0;
public function get proxyServerUrl() : String {
return _proxyServerUrl;
}
public function get author() : String {
return _login
}
public function AbstractDataSource() {
var configLoader:URLLoader = new URLLoader();
configLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
config = XML(configLoader.data);
_proxyServerUrl = XML(configLoader.data).proxyServer.toString();
});
configLoader.load(new URLRequest(configUrl));
var propertiesLoader:URLLoader = new URLLoader();
propertiesLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
properties = XML(propertiesLoader.data);
});
propertiesLoader.load(new URLRequest(propertiesUrl));
var defaultConfigLoader:URLLoader = new URLLoader();
defaultConfigLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
defaultConfig = XML(defaultConfigLoader.data);
});
defaultConfigLoader.load(new URLRequest(defaultConfigUrl));
}
public function get hasChanges() : Boolean {
return _hasChanges;
}
public function login() : void {
var authorLoader:URLLoader = new URLLoader();
authorLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
var authorResponse:XML = XML(authorLoader.data);
_login = authorResponse.Name.toString();
removeChangeHandlers();
var initialDataLoader:URLLoader = new URLLoader();
initialDataLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
var dataResponse:XML = XML(initialDataLoader.data);
parseLoadingResponse(dataResponse);
buildTrees();
addChangeHandlers();
dispatchEvent(new LoginEvent(author));
});
var dataRequestUrl:String = config.server + "request=GetEntities&brief=true";
initialDataLoader.load(new URLRequest(dataRequestUrl));
});
var requestUrl:String = config.server + "request=GetAuthor";
authorLoader.load(new URLRequest(requestUrl));
}
private function emptyCollections(collections:Array) : void {
for each(var obj:Object in collections) {
if(obj is DataElement) {
DataElement(obj).collection.disableCollectionEvent = true;
DataElement(obj).collection.removeAll();
DataElement(obj).collection.disableCollectionEvent = false;
}
}
}
public function logout() : void {
if(entities) {
emptyCollections(entities);
}
if(trees) {
for each(var obj:Object in trees) {
if(obj is Tree) {
Tree(obj).cleanup();
}
}
}
}
private function getGroupName(elementName:Object) : Object {
if(elementName is QName) {
var qname:QName = QName(elementName);
return new QName(qname.uri, qname.localName + "s");
} else {
return elementName.toString() + "s";
}
}
private function addItemsToTransaction(entityElement:XML, itemList:XMLList) : void {
for each(var item:XML in itemList) {
var entity:Entity = getEntity(item.name());
if(entity.refs) {
item = item.copy();
for each(var obj:Object in entity.refs) {
if(obj is EntityRef) {
var entityRef:EntityRef = EntityRef(obj);
var group:XML = item.child(entityRef.elementGroupName)[0];
if(group != null) {
group.setChildren(group.children().(attribute("status").length() == 0 || @status != "deleted"));
}
}
}
}
entityElement.appendChild(item);
}
}
protected function createTransaction() : XML {
var transaction:XML = ;
if(entities) {
for each(var obj:Object in entities) {
if(obj is Entity) {
var entity:Entity = Entity(obj);
var groupName:Object = getGroupName(entity.elementName);
var changed:XMLList = entity.collection.source.(@status == 'changed');
if(changed.length() > 0) {
var updateList:XMLList = transaction.child("Update");
if(updateList.length() == 0) {
transaction.appendChild();
updateList = transaction.Update;
}
var entityElement:XML = <{groupName}/>;
updateList[0].appendChild(entityElement);
addItemsToTransaction(entityElement, changed);
}
var deleted:XMLList = entity.collection.source.(@status == "deleted"
&& @clientId == "false");
if(deleted.length() > 0) {
var deleteList:XMLList = transaction.child("Delete");
if(deleteList.length() == 0) {
transaction.appendChild();
deleteList = transaction.Delete;
}
entityElement = <{groupName}/>;
deleteList[0].appendChild(entityElement);
entityElement.setChildren(deleted);
}
var neww:XMLList = entity.collection.source.(@status == "new");
if(neww.length() > 0) {
var insertList:XMLList = transaction.child("Insert");
if(insertList.length() == 0) {
transaction.appendChild();
insertList = transaction.Insert;
}
entityElement = <{groupName}/>;
insertList[0].appendChild(entityElement);
addItemsToTransaction(entityElement, neww);
}
}
}
}
return transaction;
}
private function performTransaction(transaction:XML) : void {
var doTransactionLoader:URLLoader = new URLLoader();
doTransactionLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
var response:XML = XML(doTransactionLoader.data);
if(response.localName() == "Exception") {
Alert.show(response.Message.toString());
} else {
processTransactionResponse(response);
// Show alert
Alert.show("Inserted : " + response.Inserted.@count + "\n" +
"Updated : " + response.Updated.@count + "\n" +
"Deleted : " + response.Deleted.@count);
}
});
var request:URLRequest = new URLRequest(config.server + "request=DoTransaction");
request.data = "" + transaction.toXMLString();
request.method = URLRequestMethod.POST;
request.contentType = "text/xml; charset=utf-8"
request.requestHeaders.push(new URLRequestHeader("Content-Encoding", "UTF-8"));
doTransactionLoader.load(request);
}
public function save() : void {
var transaction:XML = createTransaction();
if(transaction.length() > 0) {
var briefItems:XMLList = transaction.descendants().(attribute("brief").length() > 0
&& @brief == "true");
if(briefItems.length() == 0) {
performTransaction(transaction);
} else {
var entities:Array = new Array();
for each(var item:XML in briefItems) {
var name:Object = item.localName();
var entity:Object = null;
for(var i:uint = 0; i < entities.length; i++) {
if(entities[i].name == name) {
entity = entities[i];
break;
}
}
if(!entity) {
entity = {name: item.localName(), ids: new Array()};
entities.push(entity);
}
entity.ids.push(item.@id);
}
getEntities(entities, true);
}
}
}
protected function getMissingRefs(refs:XMLList, elementName:Object) : XMLList {
var collection:XMLListCollection = getCollection(elementName);
var retval:XMLList = new XMLList();
for(var i:int = 0; i < collection.length; i++) {
var item:XML = XML(collection.getItemAt(i));
if(refs.(@id == item.@id && @clientId == item.@clientId).length() == 0) {
retval += item;
}
}
return retval;
}
public function getUnreferredItems(parent:XML, elementName:Object) : XMLList {
var props:XML = getProps(parent);
var refs:XMLList = props.descendants(elementName);
return getMissingRefs(refs, elementName);
}
public function getRef(node:XML, status:String = null) : XML {
var retval:XML = <{node.name()}/>;
retval.@clientId = node.@clientId;
retval.@id = node.@id;
retval.@status = status == null ? node.@status : status;
return retval;
}
private function getEntities(entities:Array, saveOnReceive:Boolean = false) : void {
var incrementalDataLoader:URLLoader = new URLLoader();
incrementalDataLoader.addEventListener(Event.COMPLETE, function(event:Event) : void {
var dataResponse:XML = XML(incrementalDataLoader.data);
removeChangeHandlers();
parseLoadingResponse(dataResponse, true);
addChangeHandlers();
if(saveOnReceive) {
save();
}
});
var dataRequestUrl:String = config.server +
"request=GetEntities&brief=false&";
for(var i:uint = 0; i < entities.length; i++) {
var entity:Entity = getEntity(entities[i].name);
if(i > 0) {
dataRequestUrl += "&";
}
dataRequestUrl += entity.requestName;
dataRequestUrl += "=";
for(var j:uint = 0; j < entities[i].ids.length; j++) {
if(j > 0) {
dataRequestUrl += ",";
}
dataRequestUrl += entities[i].ids[j];
}
}
incrementalDataLoader.load(new URLRequest(dataRequestUrl));
}
public function getUsageProps(node:XML) : XML {
var entity:Entity = getEntity(node.name());
return entity.collection.source.(compareUsageRef(valueOf(), node))[0];
}
public function getProps(node:XML, acceptBrief:Boolean = true) : XML {
if(!node) {
return null;
}
var collection:XMLListCollection = getCollectionForNode(node);
if(collection) {
var list:XMLList = collection.source.(@clientId == node.@clientId
&& @id == node.@id);
if(list != null && list.length() > 0) {
var retval:XML = list[0];
if(retval.@brief == "true" && !acceptBrief) {
getEntities([{name: retval.localName(), ids: [node.@id]}]);
}
return retval;
}
}
return null;
}
public function isOwner(item:XML):Boolean
{
var props:XML = item.child("Owner").length() == 0 ? getProps(item)[0] : item;
if(props != null && props.Owner.length() > 0) {
return props.Owner == _login;
}
return false;
}
public function isUnique(type:Object, name:String, newElement:Boolean = false):Boolean {
var count:Number = newElement ? 0 : 1;
var entity:Entity = getEntity(type);
if(entity) {
return entity.collection.source.(Name == name).length() == count;
}
// Unknown entity -> no unique check to perform
return true;
}
public function deleteItem(item:XML) : void {
var props:XML = getProps(item);
props.@status = "deleted";
if(trees) {
for each(var obj:Object in trees) {
if(obj is Tree) {
var tree:Tree = Tree(obj);
for each(var ref:XML in tree.collection.source.descendants(item.localName()).(
attribute("clientId").length() > 0
&& @clientId == props.@clientId
&& @id == props.@id)) {
//removed: caused problems
//ref.setChildren(new XMLList());
ref.@status = "deleted";
}
}
}
}
var entity:Entity = getEntity(item.name());
if(entity) {
if(entity.refs) {
for each(var refObj:Object in entity.refs) {
if(refObj is EntityRef) {
var entityRef:EntityRef = refObj as EntityRef;
var refs:XMLList = props.child(entityRef.elementGroupName).children();
for each(var currentRef:XML in refs) {
var refProps:XML = getProps(currentRef);
if(refProps.attribute("useCount").length() > 0) {
refProps.@useCount--;
}
}
}
}
}
}
if(entities) {
for each(obj in entities) {
if(obj is Entity) {
entity = obj as Entity;
if(entity.refs) {
for each(refObj in entity.refs) {
if(refObj is EntityRef) {
entityRef = refObj as EntityRef;
if(entityRef.elementName == item.name()) {
var list:XMLList = entity.collection.source.child(entityRef.elementGroupName)
.children();
for each(currentRef in list.(@id == item.@id &&
@clientId == item.@clientId)) {
currentRef.@status = "deleted";
}
}
}
}
}
}
}
}
}
protected function getEntityRef(item:XML, parent:XML) : EntityRef {
var entity:Entity = getEntity(parent.name());
if(entity.refs) {
for each(var currentRef:Object in entity.refs) {
if(EntityRef(currentRef).elementName == item.name()) {
return EntityRef(currentRef);
}
}
}
return null;
}
protected function compareUsageRef(a:XML, b:XML) : Boolean {
if(a.localName() != b.localName()) {
return false;
}
return a.Name == b.Name;
}
public function removeRef(item:XML, parent:XML) : void {
var itemProps:XML = getProps(item);
var props:XML = getProps(parent);
// Remove usage
var refList:XMLList = itemProps.Usage.children().(compareUsageRef(valueOf(), props));
var collection:ExtendedXMLListCollection = getEntity(item.name()).collection;
collection.disableCollectionEvent = true;
for each(var refItem:XML in refList) {
refItem.@status = "deleted";
}
collection.disableCollectionEvent = false;
itemProps.@useCount = itemProps.Usage.children().(attribute("status").length() == 0 ||
@status != "deleted").length();
// Remove reference
var entityRef:EntityRef = getEntityRef(item, parent);
if(entityRef) {
var refs:XMLList = props.child(entityRef.elementGroupName)
.children().(@id == item.@id && @clientId == item.@clientId);
for each(var currentItem:XML in refs) {
refs.@status = "deleted";
}
if(props.@status != 'new') {
props.@status = 'changed';
}
}
}
public function moveRef(item:XML, oldParent:XML, newParent:XML, children:XMLList) : void {
var oldParentProps:XML = getProps(oldParent);
var newParentProps:XML = getProps(newParent);
var itemProps:XML = getProps(item);
var entityRef:EntityRef = getEntityRef(item, newParent);
if(!children || children.length() == 0) {
var group:XML = newParentProps.child(entityRef.elementGroupName)[0];
group.appendChild(item);
}
// disable events
for each(var obj:Object in entities) {
if(obj is Entity) {
Entity(obj).collection.disableCollectionEvent = true;
}
}
if(entityRef && children && children.length() > 0) {
group = newParentProps.child(entityRef.elementGroupName)[0];
var newChildren:XMLList = new XMLList();
for each(var currentItem:XML in children) {
newChildren += getRef(currentItem);
}
group.setChildren(newChildren);
}
if(oldParent != newParent) {
entityRef = getEntityRef(item, oldParent);
if(entityRef) {
group = oldParentProps.child(entityRef.elementGroupName)[0];
group.setChildren(group.children().(!(@id == item.@id && @clientId == item.@clientId)));
}
}
// update usage
if(itemProps.child("Usage").length() > 0) {
itemProps.Usage[0].setChildren(itemProps.Usage.(!compareUsageRef(valueOf(), oldParent)));
} else {
itemProps.appendChild();
}
itemProps.Usage[0].appendChild(getUsageRef(newParent));
// enable events
for each(obj in entities) {
if(obj is Entity) {
Entity(obj).collection.disableCollectionEvent = false;
}
}
if(newParentProps.@status != "new") {
newParentProps.@status = "changed";
}
if(oldParentProps != newParent && oldParentProps.@status != "new") {
oldParentProps.@status = "changed";
}
}
protected function getUsageRef(item:XML, newItem:Boolean = false) : XML {
var retval:XML = item.copy();
retval.setChildren(new XMLList());
if(retval.attribute("brief").length() > 0) {
delete retval.@brief;
}
if(!newItem) {
item = getProps(item);
}
retval.Owner = item.Owner;
if(item.child("Name").length() > 0) {
retval.Name = item.Name;
}
if(item.child("Titles").length() > 0 ) {
retval.Titles = item.Titles;
}
return retval;
}
public function addRef(item:XML, parent:XML) : void {
var parentProps:XML = getProps(parent);
var itemProps:XML = getProps(item);
var itemCollection:ExtendedXMLListCollection = getEntity(item.name()).collection;
itemCollection.disableCollectionEvent = true;
// Add usage
if(itemProps.child("Usage").length() == 0) {
itemProps.appendChild();
}
itemProps.Usage[0].appendChild(getUsageRef(parentProps));
itemCollection.disableCollectionEvent = false;
itemProps.@useCount++;
// Add reference
var entityRef:EntityRef = getEntityRef(itemProps, parent);
if(entityRef) {
var group:XML = parentProps.child(entityRef.elementGroupName)[0];
group.appendChild(getRef(item, "new"));
if(parentProps.@status != 'new') {
parentProps.@status = 'changed';
}
}
}
protected function createUniqueItem(item:XML) : void {
var entity:Entity = getEntity(item.name());
while(entity.collection.source.(Name == item.Name).length() > 0) {
item.Name = item.Name + "_copy";
}
}
public function duplicateItem(item:XML) : XML {
item = getProps(item).copy();
item.@status = "new";
item.@useCount = 0;
if(item.child("Usage").length() > 0) {
item.Usage[0].setChildren(new XMLList());
}
createUniqueItem(item);
var itemRef:XML = addItem(item);
// recreate references (to force consistency between references and usage references)
var entity:Entity = getEntity(item.name());
if(entity.refs) {
for each(var refObj:Object in entity.refs) {
if(refObj is EntityRef) {
var ref:EntityRef = refObj as EntityRef;
var group:XML = item.child(ref.elementGroupName)[0];
var refList:XMLList = group.children().copy();
group.setChildren(new XMLList());
for each(var currentRef:XML in refList) {
addRef(currentRef, item);
}
}
}
}
return itemRef;
}
public function addItem(item:XML) : XML {
item.@clientId = true;
item.@id = lastClientId++;
var collection:XMLListCollection = getCollectionForNode(item)
if(collection) {
collection.addItem(item);
}
return getRef(item);
}
protected function addChangeHandlers() : void {
if(!entities) {
return;
}
for each(var obj:Object in entities) {
if(obj is DataElement) {
DataElement(obj).collection.addEventListener(CollectionEvent.COLLECTION_CHANGE,
changeHandler);
}
}
}
protected function removeChangeHandlers() : void {
if(!entities) {
return;
}
for each(var obj:Object in entities) {
if(obj is DataElement) {
DataElement(obj).collection.removeEventListener(CollectionEvent.COLLECTION_CHANGE,
changeHandler);
}
}
}
protected function changeHandler(event:CollectionEvent) : void {
if(event.kind == "update") {
for each(var item:Object in event.items) {
if(item is PropertyChangeEvent) {
var changeEvent:PropertyChangeEvent = PropertyChangeEvent(item);
if(changeEvent.property != "@status" && changeEvent.property != "@useCount") {
//if(changeEvent.newValue != changeEvent.oldValue) {
var status:String = changeEvent.source.@status;
if(status != "new") {
changeEvent.source.@status = "changed"
}
//}
} else {
if(trees != null) {
for each(var obj:Object in trees) {
if(obj is Tree) {
var tree:Tree = Tree(obj);
var node:XML = XML(changeEvent.source);
for each(var ref:XML in tree.collection.source.descendants(
node.name()).(@id == node.@id
&& @clientId == node.@clientId)) {
ref.@status = node.@status;
}
}
}
}
}
}
}
}
}
protected function getEntity(name:Object) : Entity {
if(entities) {
for each(var obj:Object in entities) {
if(obj is Entity) {
var entity:Entity = Entity(obj);
if(entity.elementName == name) {
return entity;
}
}
}
}
return null;
}
protected function getCollection(name:Object) : XMLListCollection {
var entity:Entity = getEntity(name);
if(entity) {
return entity.collection;
}
return null;
}
protected function getCollectionForNode(node:XML) : XMLListCollection {
return getCollection(node.name());
}
protected function buildTrees() : void {
if(trees != null) {
for each(var obj:Object in trees) {
if(obj is Tree) {
var tree:Tree = Tree(obj);
tree.buildTree();
}
}
}
}
protected function setAllDefaultAttributes(dataResponse:XML) : void {
if(entities != null) {
for each(var obj:Object in entities) {
if(obj is Entity) {
var entity:Entity = Entity(obj);
var entities:XMLList = dataResponse.descendants(entity.elementName);
for each(var node:XML in entities) {
setDefaultAttributes(node);
}
}
}
}
}
protected function setDefaultAttributes(node:XML) : void {
node.@clientId = "false";
node.@status = "existing";
}
protected function processTransactionResponse(dataResponse:XML) : void {
var clientIdNodes:XMLList = new XMLList();
if(trees) {
for each(var obj:Object in trees) {
if(obj is Tree) {
var tree:Tree = Tree(obj);
clientIdNodes += tree.collection.source.descendants().(
attribute("clientId").length() > 0 && @clientId == "true");
}
}
}
for each(var insertNode:XML in dataResponse.Inserted.children()) {
var localName:Object = insertNode.localName();
var id:String = insertNode.@id;
var clientId:String = insertNode.@clientId;
var props:XML = getProps(<{localName} clientId='true' id={clientId}/>);
props.@clientId = false;
props.@id = id;
props.@status = "existing";
for each(item in clientIdNodes.(copy().localName() == localName
&& @id == clientId)) {
item.@clientId = false;
item.@id = id;
item.@status = "existing";
}
}
if(entities) {
for each(obj in entities) {
if(obj is Entity) {
var entity:Entity = Entity(obj);
var cursor:IViewCursor = entity.collection.createCursor();
while (!cursor.afterLast)
{
var current:XML = XML(cursor.current);
if(current.child("Usage").length() > 0) {
entity.collection.disableCollectionEvent = true;
current.Usage[0].setChildren(current.Usage.children().(
attribute("status").length() == 0 || @status != "deleted"));
entity.collection.disableCollectionEvent = false;
}
status = current.@status;
if(status == "deleted") {
cursor.remove();
} else {
if(status != "existing") { // prevents unnecessary update events
if(entity.refs) {
for each(var refObj:Object in entity.refs) {
if(refObj is EntityRef) {
var entityRef:EntityRef = EntityRef(refObj);
var group:XML = current.child(entityRef.elementGroupName)[0];
if(group != null) {
entity.collection.disableCollectionEvent = true;
group.setChildren(group.children().(attribute("status").length() == 0
|| @status != "deleted"));
for each(var ref:XML in group.descendants()
.(attribute("clientId").length() > 0
&& @clientId == "true")) {
ref.@clientId = false;
ref.@id = dataResponse.Inserted.descendants(ref.name())
.(@clientId = ref.@id)[0].@id;
}
entity.collection.disableCollectionEvent = false;
}
}
}
}
current.@status = "existing";
}
cursor.moveNext();
}
}
}
}
}
if(trees) {
for each(obj in trees) {
if(obj is Tree) {
tree = Tree(obj);
for each(var item:XML in tree.collection.source.descendants().(
attribute("status").length() > 0 && @status != "existing")) {
var status:String = item.@status;
switch(status) {
case "new":
case "changed":
item.@status = "existing";
default:
}
}
for each(item in tree.collection.source.descendants().(
children().(attribute("status").length() > 0
&& @status == "deleted").length() > 0)) {
item.setChildren(item.children().(@status != "deleted"));
}
}
}
}
}
protected function addNode(node:XML, collection:XMLListCollection) : void {
collection.addItem(node);
}
protected function parseLoadingResponse(dataResponse:XML, incremental:Boolean = false, undo:Boolean = false) : void {
setAllDefaultAttributes(dataResponse);
if(entities != null) {
for each(var obj:Object in entities) {
if(obj is Entity) {
var entity:Entity = Entity(obj);
var entities:XMLList = dataResponse.children().child(entity.elementName);
for each(var node:XML in entities) {
if(incremental) {
var props:XML = getProps(node);
if(undo) {
props.setChildren(node.children());
} else {
var newChildren:XMLList = new XMLList();
for each(var child:XML in node.children()) {
var currentChild:XMLList = props.child(child.localName());
if(currentChild.length() == 0) {
newChildren += child;
} else {
// Copy existing Usage refs
if(child.name() == "Usage") {
child = child.copy();
for each(var usageRef:XML in currentChild.children()) {
child.appendChild(usageRef);
}
newChildren += child;
} else {
newChildren += currentChild;
}
}
}
props.setChildren(newChildren);
}
props.@brief = false;
} else {
addNode(node, entity.collection);
}
}
}
}
}
}
}
}