#region Disclaimer / License
// Copyright (C) 2010, Jackie Ng
// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie@gmail.com
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using OSGeo.MapGuide.MaestroAPI.Resource;
namespace OSGeo.MapGuide.MaestroAPI.CrossConnection
{
///
/// A helper class that copies/moves resources from one
/// connection to another
///
public class ResourceMigrator
{
private IServerConnection _source;
private IServerConnection _target;
///
/// Initializes a new instance of the class.
///
/// The source.
/// The target.
public ResourceMigrator(IServerConnection source, IServerConnection target)
{
Check.NotNull(source, "source");
Check.NotNull(target, "target");
_source = source;
_target = target;
}
///
/// Copies resources from the source connection to another connection. The resources in question will
/// be copied to the specified folder. Folder structure of the source is discarded
///
///
///
///
///
///
public int CopyResources(string[] resourceIds, string folderId, bool overwrite, LengthyOperationProgressCallBack callback)
{
Check.NotNull(resourceIds, "resourceIds");
Check.NotEmpty(folderId, "folderId");
var cb = callback;
if (cb == null)
{
cb = new LengthyOperationProgressCallBack((s, e) =>
{
//Do nothing
});
}
int copied = 0;
int unit = 100 / resourceIds.Length;
int progress = 0;
foreach (var resId in resourceIds)
{
string targetId = folderId + ResourceIdentifier.GetName(resId) + "." + ResourceIdentifier.GetResourceType(resId);
string message = "";
IResource res = _source.ResourceService.GetResource(resId);
//Skip if target exists and overwrite is not specified
if (!overwrite && _target.ResourceService.ResourceExists(targetId))
{
continue;
}
else
{
//Save resource
_target.ResourceService.SaveResourceAs(res, targetId);
//Copy resource data
foreach (var data in res.EnumerateResourceData())
{
using (var stream = res.GetResourceData(data.Name))
{
stream.Position = 0L;
_target.ResourceService.SetResourceData(targetId, data.Name, data.Type, stream);
}
}
copied++;
message = string.Format(Properties.Resources.CopiedResource, resId);
}
progress += unit;
cb(this, new LengthyOperationProgressArgs(message, progress));
}
return copied;
}
///
/// Moves resources from the source connection to the specified folder on the target connection. Folder structure of the source is discarded
///
///
///
///
///
///
public int MoveResources(string[] resourceIds, string folderId, bool overwrite, LengthyOperationProgressCallBack callback)
{
Check.NotNull(resourceIds, "resourceIds");
Check.NotEmpty(folderId, "folderId");
var cb = callback;
if (cb == null)
{
cb = new LengthyOperationProgressCallBack((s, e) =>
{
//Do nothing
});
}
int moved = 0;
int unit = 100 / resourceIds.Length;
int progress = 0;
foreach (var resId in resourceIds)
{
string targetId = folderId + ResourceIdentifier.GetName(resId) + "." + ResourceIdentifier.GetResourceType(resId);
string message = "";
IResource res = _source.ResourceService.GetResource(resId);
//Skip if target exists and overwrite is not specified
if (!overwrite && _target.ResourceService.ResourceExists(targetId))
{
continue;
}
else
{
//Save resource
_target.ResourceService.SaveResourceAs(res, targetId);
//Copy resource data
foreach (var data in res.EnumerateResourceData())
{
using (var stream = res.GetResourceData(data.Name))
{
stream.Position = 0L;
_target.ResourceService.SetResourceData(targetId, data.Name, data.Type, stream);
}
}
moved++;
_source.ResourceService.DeleteResource(resId);
message = string.Format(Properties.Resources.CopiedResource, resId);
}
progress += unit;
cb(this, new LengthyOperationProgressArgs(message, progress));
}
return moved;
}
///
/// Migrates a specific resource (and its dependent resources) to the target connection
///
/// The id of the resource to migrate
/// The array of dependent resource ids
/// If true, all dependent resources that already exist in the target connection are overwritten, otherwise these are not copied over
/// A callback method to indicate progress
/// An array of resource ids that were succesfully migrated
public string[] MigrateResource(string resourceId, string[] dependentResourceIds, bool overwrite, LengthyOperationProgressCallBack callback)
{
Check.NotEmpty(resourceId, "resourceId");
Check.NotNull(dependentResourceIds, "dependentResourceIds");
//TODO: Figure out a more elegant strategy of handling saving resources
//to older versions (downgrading?)
//TODO: This should not return a string array, it should return an array
//of migration results. This requires a new API (Capability?) to test whether a resource
//can be saved to this connection
List migrated = new List();
LengthyOperationProgressCallBack cb = callback;
if (cb == null)
{
cb = new LengthyOperationProgressCallBack((o, a) => { });
}
int total = dependentResourceIds.Length + 1;
int unit = 100 / total;
int progress = 0;
try
{
//Copy the specified resource
IResource res = _source.ResourceService.GetResource(resourceId);
_target.ResourceService.SaveResource(res);
//Copy its resource data
foreach (var data in res.EnumerateResourceData())
{
using (var stream = res.GetResourceData(data.Name))
{
stream.Position = 0L;
_target.ResourceService.SetResourceData(resourceId, data.Name, data.Type, stream);
}
}
migrated.Add(resourceId);
}
catch //This happens if we're saving a resource to an older version where this resource version does not exist
{
}
//If the first one failed, abort early. Don't bother with the rest
if (migrated.Count == 1)
{
progress += unit;
cb(this, new LengthyOperationProgressArgs(string.Format(Properties.Resources.CopiedResource, resourceId), progress));
//Now copy dependents
foreach (var resId in dependentResourceIds)
{
bool existsOnTarget = _target.ResourceService.ResourceExists(resId);
if ((existsOnTarget && overwrite) || !existsOnTarget)
{
try
{
//Copy the specified resource
IResource res = _source.ResourceService.GetResource(resId);
_target.ResourceService.SaveResource(res);
//Copy its resource data
foreach (var data in res.EnumerateResourceData())
{
using (var stream = res.GetResourceData(data.Name))
{
stream.Position = 0L;
_target.ResourceService.SetResourceData(resId, data.Name, data.Type, stream);
}
}
migrated.Add(resId);
}
catch //This happens if we're saving a resource to an older version where this resource version does not exist
{
}
progress += unit;
cb(this, new LengthyOperationProgressArgs(string.Format(Properties.Resources.CopiedResource, resId), progress));
}
}
}
return migrated.ToArray();
}
///
/// Shortcut API to migrate a specific resource to the target connection. Dependent resources are automatically
/// migrated as well. This copies all dependent resources of the specified resource.
///
/// The id of the resource to migrate
/// If true, all dependent resources that already exist in the target connection are overwritten, otherwise these are not copied over
/// A callback method to indicate progress
/// An array of resource ids that were succesfully migrated
public string[] MigrateResource(string resourceId, bool overwrite, LengthyOperationProgressCallBack callback)
{
Check.NotEmpty(resourceId, "resourceId");
Dictionary resIds = new Dictionary();
var refList = GetReverseReferences(resourceId);
BuildFullDependencyList(resIds, refList);
return MigrateResource(resourceId, new List(resIds.Keys).ToArray(), overwrite, callback);
}
private List GetReverseReferences(string id)
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
using (var ms = _source.ResourceService.GetResourceXmlData(id))
{
doc.Load(ms);
}
List> refs = Utility.GetResourceIdPointers(doc);
List dependentIds = new List();
foreach (KeyValuePair s in refs)
if (!dependentIds.Contains(s.Value))
dependentIds.Add(s.Value);
return dependentIds;
}
private void BuildFullDependencyList(Dictionary resIds, IEnumerable resourceIds)
{
foreach (var id in resourceIds)
{
resIds[id] = id;
var refList = GetReverseReferences(id);
BuildFullDependencyList(resIds, refList);
}
}
///
/// Gets the source connection
///
public IServerConnection Source
{
get { return _source; }
}
///
/// Gets the target connection
///
public IServerConnection Target
{
get { return _target; }
}
}
}