#region Disclaimer / License // Copyright (C) 2009, Kenneth Skovhede // http://www.hexad.dk, opensource@hexad.dk // // 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 Disclaimer / License using GeoAPI.Geometries; using OSGeo.MapGuide.MaestroAPI.Commands; using OSGeo.MapGuide.MaestroAPI.CoordinateSystem; using OSGeo.MapGuide.MaestroAPI.Exceptions; using OSGeo.MapGuide.MaestroAPI.Feature; using OSGeo.MapGuide.MaestroAPI.Mapping; using OSGeo.MapGuide.MaestroAPI.Schema; using OSGeo.MapGuide.MaestroAPI.SchemaOverrides; using OSGeo.MapGuide.MaestroAPI.Serialization; using OSGeo.MapGuide.ObjectModels; using OSGeo.MapGuide.ObjectModels.Common; using OSGeo.MapGuide.ObjectModels.IO; using OSGeo.MapGuide.ObjectModels.LayerDefinition; using OSGeo.MapGuide.ObjectModels.LoadProcedure; using OSGeo.MapGuide.ObjectModels.MapDefinition; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.IO; using System.Text; using ObjCommon = OSGeo.MapGuide.ObjectModels.Common; namespace OSGeo.MapGuide.MaestroAPI { /// <summary> /// Base class of all connection classes. Covers functionality encompassed by /// the MapGuide Geospatial Platform API (ie. Feature Service and Resource Service) /// </summary> public abstract class PlatformConnectionBase { /// <summary> /// A list of cached serializers /// </summary> protected Hashtable m_serializers; /// <summary> /// The current XML validator /// </summary> protected XmlValidator m_validator; /// <summary> /// The path of Xsd schemas /// </summary> protected string m_schemasPath; /// <summary> /// A lookup table for Xsd Schemas /// </summary> protected Hashtable m_cachedSchemas; /// <summary> /// A flag indicating if Xsd validation is perfomed /// </summary> protected bool m_disableValidation = false; /// <summary> /// Initializes a new instance of the <see cref="PlatformConnectionBase"/> class. /// </summary> protected PlatformConnectionBase() { m_serializers = new Hashtable(); m_validator = new XmlValidator(); m_cachedSchemas = new Hashtable(); m_schemasPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "Schemas"); //NOXLATE } #region Serialization plumbing /// <summary> /// Deserializes an object from a stream. /// </summary> /// <typeparam name="T">The expected object type</typeparam> /// <param name="data">The stream containing the object</param> /// <returns>The deserialized object</returns> public virtual T DeserializeObject<T>(Stream data) => (T)DeserializeObject(typeof(T), data); /// <summary> /// Deserializes an object from a stream. /// </summary> /// <param name="type">The expected object type</param> /// <param name="data">The stream containing the object</param> /// <returns>The deserialized object</returns> public virtual object DeserializeObject(Type type, Stream data) { //HACK: MGOS 2.2 outputs different capabilities xml (because it's actually the correct one!), so //without breaking support against 2.1 and older servers, we transform the xml to its pre-2.2 form if (type == typeof(ObjectModels.Capabilities.v1_0_0.FdoProviderCapabilities) && this.SiteVersion < new Version(2, 2)) { StringBuilder sb = null; using (StreamReader reader = new StreamReader(data)) { sb = new StringBuilder(reader.ReadToEnd()); } //Pre-2.2 the elements were suffixed with Collection, change the suffix to List sb.Replace("<FunctionDefinitionCollection>", "<FunctionDefinitionList>"); //NOXLATE sb.Replace("</FunctionDefinitionCollection>", "</FunctionDefinitionList>"); //NOXLATE sb.Replace("<FunctionDefinitionCollection/>", "<FunctionDefinitionList/>"); //NOXLATE sb.Replace("<ArgumentDefinitionCollection>", "<ArgumentDefinitionList>"); //NOXLATE sb.Replace("</ArgumentDefinitionCollection>", "</ArgumentDefinitionList>"); //NOXLATE sb.Replace("<ArgumentDefinitionCollection/>", "<ArgumentDefinitionList/>"); //NOXLATE byte[] bytes = Encoding.UTF8.GetBytes(sb.ToString()); //Replace the original input stream data = new MemoryStream(bytes); } //Must copy stream, because we will be reading it twice :( //Once for validation, and once for deserialization System.IO.MemoryStream ms = new System.IO.MemoryStream(); Utility.CopyStream(data, ms); ms.Position = 0; #if DEBUG_LASTMESSAGE //Save us a copy for later investigation using (System.IO.FileStream fs = System.IO.File.Open("lastResponse.xml", System.IO.FileMode.Create, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None)) //NOXLATE Utility.CopyStream(ms, fs); ms.Position = 0; #endif //TODO: Find out why the "xs:include" doesn't work with validator //Validation is quite important, as we otherwise may end up injecting malicious code // if (!m_disableValidation) // { // m_validator.Validate(ms, GetSchema(type)); // ms.Position = 0; // } try { return GetSerializer(type).Deserialize(ms); } catch (Exception ex) { string s = ex.Message; throw; } } /// <summary> /// Serialize an object into a new memory stream. /// </summary> /// <param name="o">The object to serialize</param> /// <returns>A memorystream with the serialized object</returns> public virtual System.IO.MemoryStream SerializeObject(object o) { MemoryStream ms = new MemoryStream(); GetSerializer(o.GetType()).Serialize(new Utf8XmlWriter(ms), o); return Utility.RemoveUTF8BOM(ms); } /// <summary> /// Serializes an object into a stream /// </summary> /// <param name="o">The object to serialize</param> /// <param name="stream">The stream to serialize into</param> public virtual void SerializeObject(object o, Stream stream) { //The Utf8 writer makes sure the Utf8 tag is in place + sets encoding to Utf8 //This is needed because the server fails when rendering maps using non utf8 xml documents //And the XmlSerializer sytem in .Net does not have a method to set the encoding attribute //This does not remove the utf8 BOM marker :( //GetSerializer(o.GetType()).Serialize(new Utf8XmlWriter(stream), o); SerializeObject(o).WriteTo(stream); } /// <summary> /// Returns an XmlSerializer for the given type /// </summary> /// <param name="type">The object type to serialize</param> /// <returns>An XmlSerializer for the given type</returns> virtual protected System.Xml.Serialization.XmlSerializer GetSerializer(Type type) { if (m_serializers[type] == null) m_serializers[type] = new System.Xml.Serialization.XmlSerializer(type); return (System.Xml.Serialization.XmlSerializer)m_serializers[type]; } #endregion Serialization plumbing #region Validation /// <summary> /// Gets or sets a flag that indicates if the Xml resources are validated before leaving and entering the server. /// </summary> public bool DisableValidation { get { return m_disableValidation; } set { m_disableValidation = value; } } /// <summary> /// Validates the current server version against the highest tested version. /// </summary> /// <param name="version">The version to validate</param> protected virtual void ValidateVersion(SiteVersion version) => ValidateVersion(new Version(version.Version)); /// <summary> /// Validates the current server version against the highest tested version. /// </summary> /// <param name="version">The version to validate</param> protected virtual void ValidateVersion(Version version) { if (version > this.MaxTestedVersion) throw new Exception("Untested with MapGuide Build > " + this.MaxTestedVersion.ToString()); //NOXLATE } #endregion Validation /// <summary> /// Gets the preview URL generator. /// </summary> /// <returns>The preview URL generator. Returns null if this connection does not support browser-based resource previews</returns> public virtual Resource.Preview.IResourcePreviewUrlGenerator GetPreviewUrlGenerator() { return null; } /// <summary> /// Gets the name of the provider of this implementation /// </summary> public abstract string ProviderName { get; } /// <summary> /// Gets a collection of name-value parameters required to create another copy /// of this connection via the <see cref="T:OSGeo.MapGuide.MaestroAPI.ConnectionProviderRegistry"/> /// </summary> /// <returns></returns> public abstract NameValueCollection CloneParameters { get; } /// <summary> /// Gets the current SessionID. /// </summary> public abstract string SessionID { get; } /// <summary> /// Gets the interface of this connection /// </summary> /// <returns></returns> protected abstract IServerConnection GetInterface(); /// <summary> /// Removes the version numbers from a providername /// </summary> /// <param name="providername">The name of the provider, with or without version numbers</param> /// <returns>The provider name without version numbers</returns> public virtual string RemoveVersionFromProviderName(string providername) => Utility.StripVersionFromProviderName(providername); /// <summary> /// Gets the Xsd schema for a given type. /// </summary> /// <param name="type">The type to get the schema for</param> /// <returns>The schema for the given type</returns> virtual protected System.Xml.Schema.XmlSchema GetSchema(Type type) { if (m_cachedSchemas[type] == null) { System.Reflection.FieldInfo fi = type.GetField("SchemaName", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public); //NOXLATE if (fi == null) throw new Exception(string.Format(Strings.ErrorTypeHasNoSchemaInfo, type)); string xsd = (string)fi.GetValue(null); using (System.IO.FileStream fs = System.IO.File.Open(System.IO.Path.Combine(m_schemasPath, xsd), System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read)) m_cachedSchemas.Add(type, System.Xml.Schema.XmlSchema.Read(fs, null)); } return (System.Xml.Schema.XmlSchema)m_cachedSchemas[type]; } /// <summary> /// Raised when a resource is added /// </summary> public event ResourceEventHandler ResourceAdded; /// <summary> /// Raised when a resource is deleted. Note if a folder is deleted, this will /// only be raised for the folder and not its children. Also note that this is /// raised on any move operations as the original source is for all intents and /// purposes, deleted. /// </summary> public event ResourceEventHandler ResourceDeleted; /// <summary> /// Raised when a resource is updated /// </summary> public event ResourceEventHandler ResourceUpdated; /// <summary> /// Raises the <see cref="ResourceAdded"/> event /// </summary> /// <param name="resId"></param> protected void OnResourceAdded(string resId) => this.ResourceAdded?.Invoke(this, new ResourceEventArgs(resId)); /// <summary> /// Raises the <see cref="ResourceDeleted"/> event /// </summary> /// <param name="resId"></param> protected void OnResourceDeleted(string resId) => this.ResourceDeleted?.Invoke(this, new ResourceEventArgs(resId)); /// <summary> /// Raises the <see cref="ResourceUpdated"/> event /// </summary> /// <param name="resId"></param> protected void OnResourceUpdated(string resId) => this.ResourceUpdated?.Invoke(this, new ResourceEventArgs(resId)); /// <summary> /// Gets or sets the collection of cached schemas. Use the object type for key, and an XmlSchema instance for value. /// </summary> public virtual Hashtable CachedSchemas { get { return m_cachedSchemas; } set { m_cachedSchemas = value; } } ~PlatformConnectionBase() { Dispose(false); } /// <summary> /// Releases unmanaged and - optionally - managed resources /// </summary> public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { } /// <summary> /// Clones this instance. /// </summary> /// <returns></returns> public abstract IServerConnection Clone(); /// <summary> /// Raised when a outbound request has been dispatched /// </summary> public event RequestEventHandler RequestDispatched; /// <summary> /// Called when [request dispatched]. /// </summary> /// <param name="data">The data.</param> protected void OnRequestDispatched(string data) { var handler = this.RequestDispatched; if (handler != null) handler(this, new RequestEventArgs(data)); } #region Resource Service /// <summary> /// Returns raw data from the server a byte array /// </summary> /// <param name="resourceID">The full resourceID to get data from</param> /// <returns>Raw data from the given resource</returns> public abstract Stream GetResourceXmlData(string resourceID); /// <summary> /// Returns an object deserialized from server data. /// Uses the ResourceID to infer the object type. /// </summary> /// <param name="resourceID">The full resourceID of the item to retrieve.</param> /// <returns>A deserialized object.</returns> public virtual IResource GetResource(string resourceID) { var stream = GetResourceXmlData(resourceID); var rt = ResourceIdentifier.GetResourceTypeAsString(resourceID); IResource o = ObjectFactory.Deserialize(rt, stream); o.ResourceID = resourceID; return o; } /// <summary> /// Deletes the resource. /// </summary> /// <param name="resourceID">The resourceid.</param> public abstract void DeleteResource(string resourceID); /// <summary> /// Writes an object into a resourceID /// </summary> /// <param name="resourceID">The resource to write into</param> /// <param name="resource">The resourcec to write</param> public virtual void WriteResource(string resourceID, object resource) { System.IO.MemoryStream ms = SerializeObject(resource); ms.Position = 0; //Validate that our data is correctly formated /*if (!m_disableValidation) { m_validator.Validate(ms, GetSchema(resource.GetType())); ms.Position = 0; }*/ #if DEBUG_LASTMESSAGE using (System.IO.Stream s = System.IO.File.Open("lastSave.xml", System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None)) Utility.CopyStream(ms, s); ms.Position = 0; #endif SetResourceXmlData(resourceID, ms); } /// <summary> /// Writes raw data into a resource. /// </summary> /// <param name="resourceID">The resourceID to write into</param> /// <param name="stream">The stream containing the data to write.</param> public virtual void SetResourceXmlData(string resourceID, System.IO.Stream stream) { SetResourceXmlData(resourceID, stream, null); int purged = PurgeCachedItemsOf(resourceID); #if DEBUG System.Diagnostics.Trace.TraceInformation($"{purged} cached items purged for {resourceID}"); //NOXLATE #endif } /// <summary> /// Gets a full list of resources in the permanent server repository (Library). /// This method returns the full catalog and should be used sparringly. /// </summary> /// <returns>A list of contained resources</returns> public virtual ResourceList GetRepositoryResources() => GetRepositoryResources(StringConstants.RootIdentifier, null, -1, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// This method limits folder recursion to the specified depth. /// </summary> /// <param name="depth">The max depth to recurse. Use -1 for no limit.</param> /// <returns>A list of contained resources</returns> public virtual ResourceList GetRepositoryResources(int depth) => GetRepositoryResources(StringConstants.RootIdentifier, null, depth, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// This method limits folder recursion to the specified depth. /// </summary> /// <param name="startingpoint">The folder from which to return items. Use null for "Library://"</param> /// <param name="depth">The max depth to recurse. Use -1 for no limit.</param> /// <returns>A list of contained resources</returns> public virtual ResourceList GetRepositoryResources(string startingpoint, int depth) => GetRepositoryResources(startingpoint, null, depth, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// This method limits folder recursion to the specified depth. /// </summary> /// <param name="startingpoint">The folder from which to return items. Use null for "Library://"</param> /// <returns>A list of contained resources</returns> public virtual ResourceList GetRepositoryResources(string startingpoint) => GetRepositoryResources(startingpoint, null, -1, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// This method limits folder recursion to the specified depth. /// </summary> /// <param name="startingpoint">The folder from which to return items. Use null for "Library://"</param> /// <param name="type">The type of resource to look for. Basically this is the resource extension, like ".MapDefinition". Use null for all resources.</param> /// <returns>A list of contained resources</returns> public virtual ResourceList GetRepositoryResources(string startingpoint, string type) => GetRepositoryResources(startingpoint, type, -1, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// This method limits folder recursion to the specified depth. /// </summary> /// <param name="startingpoint">The folder from which to return items. Use null for "Library://"</param> /// <param name="type">The type of resource to look for. Basically this is the resource extension, like ".MapDefinition". Use null for all resources.</param> /// <param name="depth">The max depth to recurse. Use -1 for no limit.</param> /// <returns>A list of contained resources</returns> public ResourceList GetRepositoryResources(string startingpoint, string type, int depth) => GetRepositoryResources(startingpoint, type, depth, true); /// <summary> /// Gets a list of resources in the permanent server repository (Library). /// </summary> /// <param name="startingpoint">The folder from which to return items. Use null for "Library://"</param> /// <param name="type">The type of resource to look for. Basically this is the resource extension, like ".MapDefinition". Use null for all resources.</param> /// <param name="depth">The max depth to recurse. Use -1 for no limit.</param> /// <param name="computeChildren">A flag indicating if the count of subfolders and resources should be calculated for leaf nodes</param> /// <returns>A list of contained resources</returns> public abstract ResourceList GetRepositoryResources(string startingpoint, string type, int depth, bool computeChildren); /// <summary> /// Forces a timestamp update of the specified resource. This is akin to /// setting the resource's content using its existing content. /// </summary> /// <param name="resourceId"></param> public virtual void Touch(string resourceId) { if (!ResourceIdentifier.IsFolderResource(resourceId)) { SetResourceXmlData(resourceId, GetResourceXmlData(resourceId)); } } /// <summary> /// Returns a boolean indicating if a given resource exists /// </summary> /// <param name="resourceID">The resource to look for</param> /// <returns>True if the resource exists false otherwise. Also returns false on error.</returns> public virtual bool ResourceExists(string resourceID) { try { string sourcefolder; if (resourceID.EndsWith("/")) //NOXLATE sourcefolder = resourceID.Substring(0, resourceID.Substring(0, resourceID.Length - 1).LastIndexOf("/") + 1); //NOXLATE else sourcefolder = resourceID.Substring(0, resourceID.LastIndexOf("/") + 1); //NOXLATE ResourceList lst = GetRepositoryResources(sourcefolder, 1); foreach (object o in lst.Items) if (o.GetType() == typeof(ResourceListResourceFolder) && ((ResourceListResourceFolder)o).ResourceId == resourceID) return true; else if (o.GetType() == typeof(ResourceListResourceDocument) && ((ResourceListResourceDocument)o).ResourceId == resourceID) return true; return false; } catch { return false; } } /// <summary> /// Updates all resource references inside an object. /// </summary> /// <param name="o">The object in which the resource references are to be updated</param> /// <param name="oldresourcepath">The current resource path, the one updating from</param> /// <param name="newresourcepath">The new resource path, the one updating to</param> /// <param name="folderupdates">True if the old and new resource path identifiers are folders, false otherwise</param> public virtual void UpdateResourceReferences(object o, string oldresourcepath, string newresourcepath, bool folderupdates) => UpdateResourceReferences(o, oldresourcepath, newresourcepath, folderupdates, new Hashtable()); /// <summary> /// Updates all resource references inside an object. /// </summary> /// <param name="o">The object in which the resource references are to be updated</param> /// <param name="oldresourcepath">The current resource path, the one updating from</param> /// <param name="newresourcepath">The new resource path, the one updating to</param> /// <param name="folderupdates">True if the old and new resource path identifiers are folders, false otherwise</param> /// <param name="visited">A hashtable with objects previously visited. Used for recursion, leave as null when calling from outside the API.</param> protected void UpdateResourceReferences(object o, string oldresourcepath, string newresourcepath, bool folderupdates, Hashtable visited) { if (o == null) return; if (visited == null) visited = new Hashtable(); //Prevent infinite recursion if (o as string == null && !o.GetType().IsPrimitive) { if (visited.ContainsKey(o)) return; else visited.Add(o, null); } if (folderupdates) { if (!oldresourcepath.EndsWith("/")) //NOXLATE oldresourcepath += "/"; //NOXLATE if (!newresourcepath.EndsWith("/")) //NOXLATE newresourcepath += "/"; //NOXLATE } //If the value is a document or fragment of a document, we still wan't to repoint it if (o as System.Xml.XmlDocument != null || o as System.Xml.XmlNode != null) { Queue<System.Xml.XmlNode> lst = new Queue<System.Xml.XmlNode>(); if (o as System.Xml.XmlDocument != null) { foreach (System.Xml.XmlNode n in (o as System.Xml.XmlDocument).ChildNodes) if (n.NodeType == System.Xml.XmlNodeType.Element) lst.Enqueue(n); } else lst.Enqueue(o as System.Xml.XmlNode); while (lst.Count > 0) { System.Xml.XmlNode n = lst.Dequeue(); foreach (System.Xml.XmlNode nx in n.ChildNodes) if (nx.NodeType == System.Xml.XmlNodeType.Element) lst.Enqueue(nx); //Anything not "ResourceId" is from the LoadProcedure if (n.Name == "ResourceId" || n.Name == "SpatialDataSourcesPath" || n.Name == "LayersPath" || n.Name == "RootPath" || n.Name == "MapsPath" || n.Name == "SymbolLibrariesPath") //NOXLATE { string current = n.InnerXml; if (folderupdates && current.StartsWith(oldresourcepath)) n.InnerXml = newresourcepath + current.Substring(oldresourcepath.Length); else if (current == oldresourcepath) n.InnerXml = newresourcepath; } foreach (System.Xml.XmlAttribute a in n.Attributes) { //Anything not "ResourceId" is from the LoadProcedure if (a.Name == "ResourceId" || n.Name == "SpatialDataSourcesPath" || n.Name == "LayersPath" || n.Name == "RootPath" || n.Name == "MapsPath" || n.Name == "SymbolLibrariesPath") //NOXLATE { string current = a.Value; if (folderupdates && current.StartsWith(oldresourcepath)) n.Value = newresourcepath + current.Substring(oldresourcepath.Length); else if (current == oldresourcepath) n.Value = newresourcepath; } } } //There can be no objects in an xml document or node, so just return immediately return; } //Try to find the object properties foreach (System.Reflection.PropertyInfo pi in o.GetType().GetProperties()) { //Only index free read-write properties are taken into account if (!pi.CanRead || !pi.CanWrite || pi.GetIndexParameters().Length != 0 || pi.GetValue(o, null) == null) continue; object v = pi.GetValue(o, null); if (v == null) continue; //If we are at a ResourceId property, update it as needed string str = v as string; IEnumerable enu = v as IEnumerable; if (str != null) { bool isResId = pi.Name == "ResourceId"; //NOXLATE if (!isResId) { //Search for attributes object[] xmlAttrs = pi.GetCustomAttributes(typeof(System.Xml.Serialization.XmlElementAttribute), false); if (xmlAttrs != null) { foreach (System.Xml.Serialization.XmlElementAttribute attr in xmlAttrs) { if (attr.Type == typeof(string) && attr.ElementName == "ResourceId") //NOXLATE { if (pi.Name == "ResourceId") //NOXLATE { isResId = true; break; } } } } } if (isResId) { string current = str; if (current != null) { if (folderupdates && current.StartsWith(oldresourcepath)) pi.SetValue(o, newresourcepath + current.Substring(oldresourcepath.Length), null); else if (current == oldresourcepath) pi.SetValue(o, newresourcepath, null); } } } else if (enu != null) { //Handle collections foreach (object ox in enu) { UpdateResourceReferences(ox, oldresourcepath, newresourcepath, folderupdates, visited); } } else { Type vt = v.GetType(); if (vt.IsArray) { //Handle arrays Array sourceArr = (Array)v; for (int i = 0; i < sourceArr.Length; i++) { UpdateResourceReferences(sourceArr.GetValue(i), oldresourcepath, newresourcepath, folderupdates, visited); } } else if (vt.IsClass) { //Handle subobjects UpdateResourceReferences(v, oldresourcepath, newresourcepath, folderupdates, visited); } } } } /// <summary> /// Moves a resource, and subsequently updates all resources pointing to the old resource path /// </summary> /// <param name="oldpath">The current resource path, the one moving from</param> /// <param name="newpath">The new resource path, the one moving to</param> /// <param name="callback">A callback delegate, being called for non progress reporting events.</param> /// <param name="progress">A callback delegate, being called for progress reporting events.</param> /// <returns></returns> public virtual bool MoveResourceWithReferences(string oldpath, string newpath, LengthyOperationCallBack callback, LengthyOperationProgressCallBack progress) { LengthyOperationProgressArgs la = new LengthyOperationProgressArgs(Strings.MovingResource, -1); if (progress != null) progress(this, la); if (la.Cancel) return false; MoveResource(oldpath, newpath, true); la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; la.Progress = -1; if (progress != null) progress(this, la); if (la.Cancel) return false; ArrayList items = new ArrayList(); Hashtable paths = new Hashtable(); //The old path does not exist, but luckily the call works anyway ResourceReferenceList rlf = EnumerateResourceReferences(oldpath); foreach (string s in rlf.ResourceId) if (!paths.ContainsKey(s)) { items.Add(new LengthyOperationCallbackArgs.LengthyOperationItem(s)); paths.Add(s, null); } la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; LengthyOperationCallbackArgs args = new LengthyOperationCallbackArgs((LengthyOperationCallbackArgs.LengthyOperationItem[])items.ToArray(typeof(LengthyOperationCallbackArgs.LengthyOperationItem))); if (callback != null) callback(this, args); if (args.Cancel) return false; if (args.Index > args.Items.Length) return true; if (args.Items.Length == 0) return true; do { LengthyOperationCallbackArgs.LengthyOperationItem item = args.Items[args.Index]; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Pending; if (callback != null) { callback(this, args); if (args.Cancel) return false; } try { System.Xml.XmlDocument d = new System.Xml.XmlDocument(); using (var ms = GetResourceXmlData(item.Itempath)) d.Load(ms); UpdateResourceReferences(d, oldpath, newpath, false); using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { d.Save(ms); ms.Position = 0; SetResourceXmlData(item.Itempath, ms); } item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Success; } catch (Exception ex) { string s = ex.Message; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Failure; } if (callback != null) { callback(this, args); if (args.Cancel) return false; } args.Index++; } while (!args.Cancel && args.Index < args.Items.Length); return !args.Cancel; } /// <summary> /// Moves a folder, and subsequently updates all resources pointing to the old resource path /// </summary> /// <param name="oldpath">The current folder path, the one moving from</param> /// <param name="newpath">The new folder path, the one moving to</param> /// <param name="callback">A callback delegate, being called for non progress reporting events.</param> /// <param name="progress">A callback delegate, being called for progress reporting events.</param> /// <returns></returns> public virtual bool MoveFolderWithReferences(string oldpath, string newpath, LengthyOperationCallBack callback, LengthyOperationProgressCallBack progress) { oldpath = FixAndValidateFolderPath(oldpath); newpath = FixAndValidateFolderPath(newpath); LengthyOperationProgressArgs la = new LengthyOperationProgressArgs(Strings.ProgressMovingFolder, -1); if (progress != null) progress(this, la); if (la.Cancel) return false; MoveFolder(oldpath, newpath, true); la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; int pg = 0; la.Progress = 0; la.StatusMessage = Strings.ProgressFindingFolderRefs; if (progress != null) progress(this, la); if (la.Cancel) return false; ResourceList lst = GetRepositoryResources(newpath); Hashtable items = new Hashtable(); foreach (object o in lst.Items) { if (o.GetType() == typeof(ResourceListResourceDocument)) { //The old path does not exist, but we need to enumerate references at the old location string resource_oldpath = ((ResourceListResourceDocument)o).ResourceId; resource_oldpath = oldpath + resource_oldpath.Substring(newpath.Length); ResourceReferenceList rlf = EnumerateResourceReferences(resource_oldpath); foreach (string s in rlf.ResourceId) if (!items.Contains(s)) items.Add(s, new LengthyOperationCallbackArgs.LengthyOperationItem(s)); } pg++; la.Progress = Math.Max(Math.Min(99, (int)(((double)pg / (double)lst.Items.Count) * (double)100)), 0); if (progress != null) progress(this, la); if (la.Cancel) return false; } la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; LengthyOperationCallbackArgs.LengthyOperationItem[] vi = new LengthyOperationCallbackArgs.LengthyOperationItem[items.Values.Count]; items.Values.CopyTo(vi, 0); LengthyOperationCallbackArgs args = new LengthyOperationCallbackArgs(vi); if (callback != null) callback(this, args); if (args.Cancel) return false; if (args.Index > args.Items.Length) return true; if (args.Items.Length == 0) return true; do { LengthyOperationCallbackArgs.LengthyOperationItem item = args.Items[args.Index]; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Pending; if (callback != null) { callback(this, args); if (args.Cancel) return false; } try { System.Xml.XmlDocument d = new System.Xml.XmlDocument(); using (var ms = GetResourceXmlData(item.Itempath)) d.Load(ms); UpdateResourceReferences(d, oldpath, newpath, true); using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { d.Save(ms); ms.Position = 0; SetResourceXmlData(item.Itempath, ms); } item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Success; } catch (Exception ex) { string s = ex.Message; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Failure; } if (callback != null) { callback(this, args); if (args.Cancel) return false; } args.Index++; } while (!args.Cancel && args.Index < args.Items.Length); return !args.Cancel; } /// <summary> /// Copies folder, and subsequently updates all resources within the folder to use the new folder path instead of the originating one. /// </summary> /// <param name="oldpath">The current folder path, the one copying from</param> /// <param name="newpath">The new folder path, the one copying to</param> /// <param name="callback">A callback delegate, being called for non progress reporting events.</param> /// <param name="progress">A callback delegate, being called for progress reporting events.</param> /// <returns></returns> public bool CopyFolderWithReferences(string oldpath, string newpath, LengthyOperationCallBack callback, LengthyOperationProgressCallBack progress) { oldpath = FixAndValidateFolderPath(oldpath); newpath = FixAndValidateFolderPath(newpath); ResourceList lst = GetRepositoryResources(oldpath); LengthyOperationProgressArgs la = new LengthyOperationProgressArgs(Strings.ProgressCopyingFolder, -1); if (progress != null) progress(this, la); if (la.Cancel) return false; CopyFolder(oldpath, newpath, true); la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; la.Progress = 0; la.StatusMessage = Strings.ProgressFindingFolderRefs; int pg = 0; if (progress != null) progress(this, la); if (la.Cancel) return false; ArrayList items = new ArrayList(); Hashtable paths = new Hashtable(); foreach (object o in lst.Items) { if (o.GetType() == typeof(ResourceListResourceDocument)) { ResourceReferenceList rlf = EnumerateResourceReferences(((ResourceListResourceDocument)o).ResourceId); foreach (string s in rlf.ResourceId) if (s.StartsWith(oldpath)) { string dest = newpath + s.Substring(oldpath.Length); if (!paths.ContainsKey(dest)) { items.Add(new LengthyOperationCallbackArgs.LengthyOperationItem(dest)); paths.Add(dest, null); } } } pg++; la.Progress = Math.Max(Math.Min(99, (int)(((double)pg / (double)lst.Items.Count) * (double)100)), 0); if (progress != null) progress(this, la); if (la.Cancel) return false; } la.Progress = 100; if (progress != null) progress(this, la); if (la.Cancel) return false; LengthyOperationCallbackArgs args = new LengthyOperationCallbackArgs((LengthyOperationCallbackArgs.LengthyOperationItem[])items.ToArray(typeof(LengthyOperationCallbackArgs.LengthyOperationItem))); if (callback != null) callback(this, args); if (args.Cancel) return false; if (args.Index > args.Items.Length) return true; if (args.Items.Length == 0) return true; do { LengthyOperationCallbackArgs.LengthyOperationItem item = args.Items[args.Index]; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Pending; if (callback != null) { callback(this, args); if (args.Cancel) return false; } try { System.Xml.XmlDocument d = new System.Xml.XmlDocument(); using (var ms = GetResourceXmlData(item.Itempath)) d.Load(ms); UpdateResourceReferences(d, oldpath, newpath, true); using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) { d.Save(ms); ms.Position = 0; SetResourceXmlData(item.Itempath, ms); } item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Success; } catch (Exception ex) { string s = ex.Message; item.Status = LengthyOperationCallbackArgs.LengthyOperationItem.OperationStatus.Failure; } if (callback != null) { callback(this, args); if (args.Cancel) return false; } args.Index++; } while (!args.Cancel && args.Index < args.Items.Length); return !args.Cancel; } /// <summary> /// Validates the origin of the folder, and ensures the folder path has a trailing slash. /// </summary> /// <param name="folderpath">The path to validate and fix</param> /// <returns>The fixed path</returns> virtual protected string FixAndValidateFolderPath(string folderpath) { if (!folderpath.StartsWith(StringConstants.RootIdentifier) && !folderpath.StartsWith("Session:" + this.SessionID + "//")) //NOXLATE throw new Exception(Strings.ErrorInvalidResourceIdentifierType); if (!folderpath.EndsWith("/")) //NOXLATE folderpath += "/"; //NOXLATE return folderpath; } /// <summary> /// Creates a folder on the server /// </summary> /// <param name="resourceID">The path of the folder to create</param> public virtual void CreateFolder(string resourceID) { resourceID = FixAndValidateFolderPath(resourceID); SetResourceXmlData(resourceID, new MemoryStream()); } /// <summary> /// Returns a value indicating if a given folder exists /// </summary> /// <param name="folderpath">The path of the folder</param> /// <returns>True if the folder exists, false otherwise. Also returns false on error.</returns> public virtual bool HasFolder(string folderpath) { folderpath = FixAndValidateFolderPath(folderpath); try { ResourceList l = this.GetRepositoryResources(folderpath, 1); return true; } catch { return false; } } /// <summary> /// Enumereates all references to a given resource /// </summary> /// <param name="resourceid">The resource to enumerate references for</param> /// <returns>A list of resources that reference the given resourceID</returns> public abstract ResourceReferenceList EnumerateResourceReferences(string resourceid); /// <summary> /// Copies a resource from one location to another. This does not update any references. /// </summary> /// <param name="oldpath">The current resource path, the one copying from</param> /// <param name="newpath">The new resource path, the one copying to</param> /// <param name="overwrite">True if the copy can overwrite an existing resource, false otherwise</param> public abstract void CopyResource(string oldpath, string newpath, bool overwrite); /// <summary> /// Copies a folder and all its content. This does not update any references. /// </summary> /// <param name="oldpath">The current folder path, the one copying from</param> /// <param name="newpath">The new folder path, the one copying to</param> /// <param name="overwrite">True if the copy can overwrite an existing folder, false otherwise</param> public abstract void CopyFolder(string oldpath, string newpath, bool overwrite); /// <summary> /// Moves a resource from one location to another. This does not update any references. /// </summary> /// <param name="oldpath">The current resource path, the one moving from</param> /// <param name="newpath">The new resource path, the one moving to</param> /// <param name="overwrite">True if the move can overwrite an existing resource, false otherwise</param> public abstract void MoveResource(string oldpath, string newpath, bool overwrite); /// <summary> /// Moves a folder and its content from one location to another. This does not update any references. /// </summary> /// <param name="oldpath">The current folder path, the one moving from</param> /// <param name="newpath">The new folder path, the one moving to</param> /// <param name="overwrite">True if the move can overwrite an existing folder, false otherwise</param> public abstract void MoveFolder(string oldpath, string newpath, bool overwrite); /// <summary> /// Returns data from a resource as a memorystream /// </summary> /// <param name="resourceID">The id of the resource to fetch data from</param> /// <param name="dataname">The name of the associated data item</param> /// <returns>A stream containing the references resource data</returns> public abstract Stream GetResourceData(string resourceID, string dataname); /// <summary> /// Uploads data to a resource /// </summary> /// <param name="resourceid">The id of the resource to update</param> /// <param name="dataname">The name of the data to update or create</param> /// <param name="datatype">The type of data</param> /// <param name="stream">A stream containing the new content of the resource data</param> public virtual void SetResourceData(string resourceid, string dataname, ResourceDataType datatype, Stream stream) => SetResourceData(resourceid, dataname, datatype, stream, null); /// <summary> /// Uploads data to a resource /// </summary> /// <param name="resourceid">The id of the resource to update</param> /// <param name="dataname">The name of the data to update or create</param> /// <param name="datatype">The type of data</param> /// <param name="stream">A stream containing the new content of the resource data</param> /// <param name="callback">The callback.</param> public abstract void SetResourceData(string resourceid, string dataname, ResourceDataType datatype, Stream stream, Utility.StreamCopyProgressDelegate callback); /// <summary> /// Removes all cached items associated with the given feature source /// </summary> /// <param name="resourceId"></param> /// <returns></returns> protected int PurgeCachedItemsOf(string resourceId) { //All keys are encoded with the resource id at the beginning, //so hunt down all matching keys starting with our resource id //these will be queued for removal. var purgeFsd = new List<string>(); foreach (var key in m_featureSchemaCache.Keys) { if (key.StartsWith(resourceId)) purgeFsd.Add(key); } var purgeCls = new List<string>(); foreach (var key in m_classDefinitionCache.Keys) { if (key.StartsWith(resourceId)) purgeCls.Add(key); } int removed = 0; foreach (var key in purgeFsd) { if (m_featureSchemaCache.Remove(key)) removed++; } foreach (var key in purgeCls) { if (m_classDefinitionCache.Remove(key)) removed++; } return removed; } /// <summary> /// Saves the resource. /// </summary> /// <param name="resource">The resource.</param> public void SaveResource(IResource resource) { try { SaveResourceAs(resource, resource.ResourceID); } catch (Exception ex) { if (Utility.IsDbXmlError(ex)) ex.Data[Utility.XML_EXCEPTION_KEY] = resource.Serialize(); throw ex; } } /// <summary> /// Saves the resource with the specified resource ID /// </summary> /// <param name="resource">The resource.</param> /// <param name="resourceid">The resourceid.</param> public void SaveResourceAs(IResource resource, string resourceid) { try { var stream = ObjectFactory.Serialize(resource); SetResourceXmlData(resourceid, stream); } catch (Exception ex) { if (Utility.IsDbXmlError(ex)) ex.Data[Utility.XML_EXCEPTION_KEY] = resource.Serialize(); throw ex; } } /// <summary> /// Upload a MapGuide Package file to the server /// </summary> /// <param name="filename">Name of the file to upload</param> /// <param name="callback">A callback argument used to display progress. May be null.</param> public abstract void UploadPackage(string filename, Utility.StreamCopyProgressDelegate callback); /// <summary> /// Updates the repository. /// </summary> /// <param name="resourceId">The resource id.</param> /// <param name="header">The header.</param> public abstract void UpdateRepository(string resourceId, ResourceFolderHeaderType header); /// <summary> /// Gets the folder or resource header. /// </summary> /// <param name="resourceId">The resource id.</param> /// <returns></returns> public abstract object GetFolderOrResourceHeader(string resourceId); /// <summary> /// Sets the resource XML data. /// </summary> /// <param name="resourceId">The resource id.</param> /// <param name="content">The content.</param> /// <param name="header">The header.</param> public abstract void SetResourceXmlData(string resourceId, System.IO.Stream content, System.IO.Stream header); /// <summary> /// Gets the resource header. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <returns></returns> public virtual ResourceDocumentHeaderType GetResourceHeader(string resourceID) => (ResourceDocumentHeaderType)this.GetFolderOrResourceHeader(resourceID); /// <summary> /// Gets the folder header. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <returns></returns> public virtual ResourceFolderHeaderType GetFolderHeader(string resourceID) { if (resourceID.EndsWith("//")) { ResourceList lst = this.GetRepositoryResources(resourceID, 0); ResourceListResourceFolder fld = lst.Items[0] as ResourceListResourceFolder; return fld.ResourceFolderHeader; } else return (ResourceFolderHeaderType)this.GetFolderOrResourceHeader(resourceID); } /// <summary> /// Sets the folder header. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="header">The header.</param> public virtual void SetFolderHeader(string resourceID, ResourceFolderHeaderType header) => SetFolderOrResourceHeader(resourceID, header); /// <summary> /// Sets the resource header. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="header">The header.</param> public virtual void SetResourceHeader(string resourceID, ResourceDocumentHeaderType header) => SetFolderOrResourceHeader(resourceID, header); /// <summary> /// Sets the folder or resource header. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="header">The header.</param> public virtual void SetFolderOrResourceHeader(string resourceID, object header) { if (header == null) throw new ArgumentNullException(nameof(header)); //NOXLATE ResourceSecurityType sec; if (header as ResourceFolderHeaderType != null) sec = (header as ResourceFolderHeaderType).Security; else if (header as ResourceDocumentHeaderType != null) sec = (header as ResourceDocumentHeaderType).Security; else throw new ArgumentException(Strings.ErrorInvalidResourceHeaderRootElement, nameof(header)); //NOXLATE if (sec.Users != null && sec.Users.User != null && sec.Users.User.Count == 0) sec.Users = null; if (sec.Groups != null && sec.Groups.Group != null && sec.Groups.Group.Count == 0) sec.Groups = null; if (resourceID.EndsWith("//")) //NOXLATE { if (header as ResourceFolderHeaderType == null) throw new Exception(string.Format(Strings.ErrorResourceMustBeUpdatedWithFolderHeader, resourceID)); UpdateRepository(resourceID, header as ResourceFolderHeaderType); } else this.SetResourceXmlData(resourceID, null, this.SerializeObject(header)); } /// <summary> /// Enumerates all unmanaged folders, meaning alias'ed folders /// </summary> /// <param name="type">The type of data to return</param> /// <param name="filter">A filter applied to the items</param> /// <param name="recursive">True if the list should contains recursive results</param> /// <param name="startpath">The path to retrieve the data from</param> /// <returns>A list of unmanaged data</returns> public abstract UnmanagedDataList EnumerateUnmanagedData(string startpath, string filter, bool recursive, UnmanagedDataTypes type); #endregion Resource Service #region Feature Service /// <summary> /// Returns an installed provider, given the name of the provider /// </summary> /// <param name="providername">The name of the provider</param> /// <returns>The first matching provider or null</returns> public virtual FeatureProviderRegistryFeatureProvider GetFeatureProvider(string providername) { string pname = RemoveVersionFromProviderName(providername).ToLower(); foreach (FeatureProviderRegistryFeatureProvider p in this.FeatureProviders) if (RemoveVersionFromProviderName(p.Name).ToLower().Equals(pname.ToLower())) return p; return null; } /// <summary> /// Tests the connection. /// </summary> /// <param name="featuresource">The featuresource.</param> /// <returns></returns> public abstract string TestConnection(string featuresource); /// <summary> /// Gets a list of installed feature providers /// </summary> public abstract FeatureProviderRegistryFeatureProvider[] FeatureProviders { get; } /// <summary> /// Returns the spatial info for a given featuresource /// </summary> /// <param name="resourceID">The ID of the resource to query</param> /// <param name="activeOnly">Query only active items</param> /// <returns>A list of spatial contexts</returns> public abstract FdoSpatialContextList GetSpatialContextInfo(string resourceID, bool activeOnly); /// <summary> /// Gets the names of the identity properties from a feature /// </summary> /// <param name="resourceID">The resourceID for the FeatureSource</param> /// <param name="classname">The classname of the feature, including schema</param> /// <returns>A string array with the found identities</returns> public abstract string[] GetIdentityProperties(string resourceID, string classname); /// <summary> /// Describes the feature source. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="schema">The schema.</param> /// <returns></returns> public abstract FeatureSchema DescribeFeatureSource(string resourceID, string schema); /// <summary> /// Describes the specified feature source restricted to only the specified schema and the specified class names /// </summary> /// <param name="resourceID"></param> /// <param name="schema"></param> /// <param name="classNames"></param> /// <returns></returns> public abstract FeatureSchema DescribeFeatureSourcePartial(string resourceID, string schema, string[] classNames); /// <summary> /// feature source description cache /// </summary> protected Dictionary<string, FeatureSourceDescription> m_featureSchemaCache = new Dictionary<string, FeatureSourceDescription>(); /// <summary> /// a class definition cache /// </summary> protected Dictionary<string, ClassDefinition> m_classDefinitionCache = new Dictionary<string, ClassDefinition>(); /// <summary> /// Calls the actual implementation of the DescribeFeatureSource API /// </summary> /// <param name="resourceId"></param> /// <returns></returns> protected abstract FeatureSourceDescription DescribeFeatureSourceInternal(string resourceId); /// <summary> /// Gets the feature source description. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <returns></returns> public virtual FeatureSourceDescription DescribeFeatureSource(string resourceID) { #if DEBUG bool bFromCache = true; #endif if (!m_featureSchemaCache.ContainsKey(resourceID)) { #if DEBUG bFromCache = false; #endif var fsd = this.DescribeFeatureSourceInternal(resourceID); try { //Cache a clone of each class definition m_featureSchemaCache[resourceID] = FeatureSourceDescription.Clone(fsd); foreach (ClassDefinition cls in fsd.AllClasses) { string classCacheKey = resourceID + "!" + cls.QualifiedName; //NOXLATE m_classDefinitionCache[classCacheKey] = cls; } } catch { m_featureSchemaCache[resourceID] = null; } } #if DEBUG if (bFromCache) System.Diagnostics.Trace.TraceInformation($"Returning cached description for {resourceID}"); //NOXLATE #endif //Return a clone to ensure immutability of cached one return FeatureSourceDescription.Clone(m_featureSchemaCache[resourceID]); } /// <summary> /// Fetches the specified class definition /// </summary> /// <param name="resourceId"></param> /// <param name="schemaName"></param> /// <param name="className"></param> /// <returns></returns> protected abstract ClassDefinition GetClassDefinitionInternal(string resourceId, string schemaName, string className); /// <summary> /// Gets the class definition. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="className">The class name to look for.</param> /// <returns></returns> public virtual ClassDefinition GetClassDefinition(string resourceID, string className) { //NOTE: To prevent ambiguity, only class definitions queried with qualified //names are cached. Un-qualified ones will call directly into the implementing //GetClassDefinition API bool bQualified = className.Contains(":"); //NOXLATE string classCacheKey = resourceID + "!" + className; //NOXLATE ClassDefinition cls = null; bool bStoreInCache = true; #if DEBUG bool bFromCache = false; #endif //We don't interrogate the Feature Source Description cache because part of //caching a Feature Source Description is to cache all the classes within if (m_classDefinitionCache.ContainsKey(classCacheKey)) { cls = m_classDefinitionCache[classCacheKey]; bStoreInCache = false; #if DEBUG bFromCache = true; #endif } else { if (bQualified) { var tokens = className.Split(':'); //NOXLATE cls = GetClassDefinitionInternal(resourceID, tokens[0], tokens[1]); } else { cls = GetClassDefinitionInternal(resourceID, null, className); } } //Only class definitions queried with qualified names can be cached if (bStoreInCache && !bQualified) bStoreInCache = false; #if DEBUG if (bFromCache) System.Diagnostics.Trace.TraceInformation($"Returning cached class ({className}) for {resourceID}"); //NOXLATE #endif if (cls != null) { //Sanity check var key = resourceID + "!" + cls.QualifiedName; //NOXLATE if (bStoreInCache && classCacheKey == key) { m_classDefinitionCache[classCacheKey] = cls; } //Return a clone of the cached object to ensure immutability of //the original return ClassDefinition.Clone(cls); } return null; } internal int CachedFeatureSources => m_featureSchemaCache.Count; internal int CachedClassDefinitions => m_classDefinitionCache.Count; /// <summary> /// Resets the feature source schema cache. /// </summary> public virtual void ResetFeatureSourceSchemaCache() { m_featureSchemaCache = new Dictionary<string, FeatureSourceDescription>(); m_classDefinitionCache = new Dictionary<string, ClassDefinition>(); } /// <summary> /// Performs an aggregate query on all columns in the datasource /// </summary> /// <param name="resourceID">The resourceID of the FeatureSource to query</param> /// <param name="schema">The schema name</param> /// <param name="filter">The filter to apply to the </param> /// <returns>A FeatureSetReader with the aggregated values</returns> public virtual IReader AggregateQueryFeatureSource(string resourceID, string schema, string filter) => AggregateQueryFeatureSource(resourceID, schema, filter, (string[])null); /// <summary> /// Performs an aggregate query on columns in the datasource /// </summary> /// <param name="resourceID">The resourceID of the FeatureSource to query</param> /// <param name="schema">The schema name</param> /// <param name="filter">The filter to apply to the </param> /// <param name="columns">The columns to aggregate</param> /// <returns>A IFeatureReader with the aggregated values</returns> public abstract IReader AggregateQueryFeatureSource(string resourceID, string schema, string filter, string[] columns); /// <summary> /// Performs an aggregate query on computed resources /// </summary> /// <param name="resourceID">The resourceID of the FeatureSource to query</param> /// <param name="schema">The schema name</param> /// <param name="filter">The filter to apply to the </param> /// <param name="aggregateFunctions">A collection of column name and aggregate functions</param> /// <returns>A FeatureSetReader with the aggregated values</returns> public abstract IReader AggregateQueryFeatureSource(string resourceID, string schema, string filter, System.Collections.Specialized.NameValueCollection aggregateFunctions); /// <summary> /// Gets the spatial extent. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="schema">The schema.</param> /// <param name="geometry">The geometry.</param> /// <returns></returns> public virtual ObjCommon.IEnvelope GetSpatialExtent(string resourceID, string schema, string geometry) => GetSpatialExtent(resourceID, schema, geometry, null, false); /// <summary> /// Gets the spatial extent. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="schema">The schema.</param> /// <param name="geometry">The geometry.</param> /// <param name="filter">The filter.</param> /// <returns></returns> public virtual ObjCommon.IEnvelope GetSpatialExtent(string resourceID, string schema, string geometry, string filter) => GetSpatialExtent(resourceID, schema, geometry, filter, false); /// <summary> /// Gets the spatial extent. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="schema">The schema.</param> /// <param name="geometry">The geometry.</param> /// <param name="allowFallbackToContextInformation">if set to <c>true</c> [allow fallback to context information].</param> /// <returns></returns> public virtual ObjCommon.IEnvelope GetSpatialExtent(string resourceID, string schema, string geometry, bool allowFallbackToContextInformation) => GetSpatialExtent(resourceID, schema, geometry, null, allowFallbackToContextInformation); /// <summary> /// Gets the spatial extent. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="schema">The schema.</param> /// <param name="geometry">The geometry.</param> /// <param name="filter">The filter.</param> /// <param name="allowFallbackToContextInformation">if set to <c>true</c> [allow fallback to context information].</param> /// <exception cref="T:OSGeo.MapGuide.MaestroAPI.Exceptions.NullExtentException">Thrown if the geometric extent is null</exception> /// <returns></returns> protected virtual ObjCommon.IEnvelope GetSpatialExtent(string resourceID, string schema, string geometry, string filter, bool allowFallbackToContextInformation) { Check.ArgumentNotEmpty(schema, nameof(schema)); Check.ArgumentNotEmpty(geometry, nameof(geometry)); try { var fun = new NameValueCollection(); fun.Add("EXTENT", $"SpatialExtents(\"{geometry}\")"); //NOXLATE IReader fsr = null; try { fsr = AggregateQueryFeatureSource(resourceID, schema, filter, fun); if (fsr.ReadNext()) { if (fsr.IsNull("EXTENT")) //NOXLATE throw new NullExtentException(); IGeometry geom = fsr["EXTENT"] as IGeometry; //NOXLATE if (geom == null) { throw new NullExtentException(); } else { var env = geom.EnvelopeInternal; return ObjectFactory.CreateEnvelope( env.MinX, env.MinY, env.MaxX, env.MaxY); } } else throw new Exception(string.Format(Strings.ErrorNoDataInResource, resourceID)); } finally { fsr?.Close(); fsr?.Dispose(); fsr = null; } } catch { if (allowFallbackToContextInformation) try { FdoSpatialContextList lst = this.GetSpatialContextInfo(resourceID, false); if (lst.SpatialContext != null && lst.SpatialContext.Count >= 1) { return ObjectFactory.CreateEnvelope( double.Parse(lst.SpatialContext[0].Extent.LowerLeftCoordinate.X, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture), double.Parse(lst.SpatialContext[0].Extent.LowerLeftCoordinate.Y, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture), double.Parse(lst.SpatialContext[0].Extent.UpperRightCoordinate.X, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture), double.Parse(lst.SpatialContext[0].Extent.UpperRightCoordinate.Y, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture) ); } } catch { } throw; } } /// <summary> /// Enumerates the data stores. /// </summary> /// <param name="providerName">Name of the provider.</param> /// <param name="partialConnString">The partial conn string.</param> /// <returns></returns> public abstract DataStoreList EnumerateDataStores(string providerName, string partialConnString); /// <summary> /// Gets the schemas. /// </summary> /// <param name="resourceId">The resource id.</param> /// <returns></returns> public abstract string[] GetSchemas(string resourceId); /// <summary> /// Gets the class names. /// </summary> /// <param name="resourceId">The resource id.</param> /// <param name="schemaName">Name of the schema.</param> /// <returns></returns> public abstract string[] GetClassNames(string resourceId, string schemaName); /// <summary> /// Gets the long transactions for the specified feature source /// </summary> /// <param name="resourceId">The feature source id</param> /// <param name="activeOnly">If true, will only return active long transactions</param> /// <returns></returns> public abstract ILongTransactionList GetLongTransactions(string resourceId, bool activeOnly); /// <summary> /// Gets the schema mappings for the given FDO provider. These mappings form the basis for a custom configuration document /// for a feature source that supports configuration /// </summary> /// <param name="provider">The FDO provider</param> /// <param name="partialConnString">The connection string</param> /// <returns></returns> public abstract ConfigurationDocument GetSchemaMapping(string provider, string partialConnString); /// <summary> /// Executes a feature query on the specified feature source /// </summary> /// <param name="resourceID">The Feature Source ID</param> /// <param name="className">The feature class name</param> /// <param name="filter">The FDO filter string that determines what features will be returned</param> /// <returns>A <see cref="T:OSGeo.MapGuide.MaestroAPI.Feature.IFeatureReader"/> containing the results of the query</returns> public IFeatureReader QueryFeatureSource(string resourceID, string className, string filter) => QueryFeatureSource(resourceID, className, filter, null); /// <summary> /// Executes a feature query on the specified feature source /// </summary> /// <param name="resourceID">The Feature Source ID</param> /// <param name="className">The feature class name</param> /// <returns>A <see cref="T:OSGeo.MapGuide.MaestroAPI.Feature.IFeatureReader"/> containing the results of the query</returns> public IFeatureReader QueryFeatureSource(string resourceID, string className) => QueryFeatureSource(resourceID, className, null, null); /// <summary> /// Executes a feature query on the specified feature source /// </summary> /// <param name="resourceID">The Feature Source ID</param> /// <param name="className">The feature class name</param> /// <param name="filter">The FDO filter string that determines what features will be returned</param> /// <param name="propertyNames">A list of properties that are to be returned in the query result</param> /// <returns>A <see cref="T:OSGeo.MapGuide.MaestroAPI.Feature.IFeatureReader"/> containing the results of the query</returns> public IFeatureReader QueryFeatureSource(string resourceID, string className, string filter, string[] propertyNames) => QueryFeatureSource(resourceID, className, filter, propertyNames, null); /// <summary> /// Executes a feature query on the specified feature source /// </summary> /// <param name="resourceID">The Feature Source ID</param> /// <param name="className">The feature class name</param> /// <param name="filter">The FDO filter string that determines what features will be returned</param> /// <param name="propertyNames">A list of properties that are to be returned in the query result</param> /// <param name="computedProperties">A list of name/value pairs that contain the alias (name) for an FDO expression (value)</param> /// <param name="limit">Limits the number of features returned in the reader. -1 for all features</param> /// <returns>A <see cref="T:OSGeo.MapGuide.MaestroAPI.Feature.IFeatureReader"/> containing the results of the query</returns> public virtual IFeatureReader QueryFeatureSource(string resourceID, string className, string filter, string[] propertyNames, NameValueCollection computedProperties, int limit) { var reader = this.QueryFeatureSource(resourceID, className, filter, propertyNames, computedProperties); if (limit < 0) return reader; else return new LimitingFeatureReader(reader, limit); } /// <summary> /// Executes a feature query on the specified feature source /// </summary> /// <param name="resourceID">The Feature Source ID</param> /// <param name="className">The feature class name</param> /// <param name="filter">The FDO filter string that determines what features will be returned</param> /// <param name="propertyNames">A list of properties that are to be returned in the query result</param> /// <param name="computedProperties">A list of name/value pairs that contain the alias (name) for an FDO expression (value)</param> /// <returns>A <see cref="T:OSGeo.MapGuide.MaestroAPI.Feature.IFeatureReader"/> containing the results of the query</returns> public abstract IFeatureReader QueryFeatureSource(string resourceID, string className, string filter, string[] propertyNames, NameValueCollection computedProperties); #endregion Feature Service #region Feature/Capability Discovery /// <summary> /// Gets the highest version the API is currently tested againts /// </summary> public virtual Version MaxTestedVersion => SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS2_1); /// <summary> /// Gets the site version. /// </summary> /// <value>The site version.</value> public abstract Version SiteVersion { get; } /// <summary> /// Gets the custom property names. /// </summary> /// <returns></returns> public abstract string[] GetCustomPropertyNames(); /// <summary> /// Gets the type of the custom property. /// </summary> /// <param name="name">The name.</param> /// <returns></returns> public abstract Type GetCustomPropertyType(string name); /// <summary> /// Sets the custom property. /// </summary> /// <param name="name">The name.</param> /// <param name="value">The value.</param> public abstract void SetCustomProperty(string name, object value); /// <summary> /// Gets the custom property. /// </summary> /// <param name="name">The name.</param> /// <returns></returns> public abstract object GetCustomProperty(string name); /// <summary> /// Creates the command. /// </summary> /// <param name="cmdType">Type of the CMD.</param> /// <returns></returns> public virtual ICommand CreateCommand(int cmdType) { CommandType ct = (CommandType)cmdType; switch (ct) { default: return null; } } #endregion Feature/Capability Discovery #region runtime map /// <summary> /// Infers the meters per unit value from the specified coordinate system /// </summary> /// <param name="csWkt"></param> /// <param name="units"></param> /// <returns></returns> protected virtual double InferMPU(string csWkt, double units) { try { var cs = CoordinateSystemBase.Create(csWkt); return cs.MetersPerUnitX * units; } catch { return 1.0; } } private class DefaultCalculator : IMpuCalculator { private readonly PlatformConnectionBase _conn; public DefaultCalculator(PlatformConnectionBase conn) { _conn = conn; } public double Calculate(string csWkt, double units) { return _conn.InferMPU(csWkt, units); } } /// <summary> /// Gets the MPU calculator /// </summary> /// <returns></returns> public virtual IMpuCalculator GetCalculator() => new DefaultCalculator(this); /// <summary> /// Creates the map group. /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="name">The name.</param> /// <returns></returns> public virtual RuntimeMapGroup CreateMapGroup(RuntimeMap parent, string name) => new RuntimeMapGroup(parent, name); //TODO: Review when we decide to split the implementations /// <summary> /// Creates a new runtime map group /// </summary> /// <param name="parent">The map.</param> /// <param name="group">The group.</param> /// <returns></returns> public virtual RuntimeMapGroup CreateMapGroup(RuntimeMap parent, IBaseMapGroup group) => new RuntimeMapGroup(parent, group); //TODO: Review when we decide to split the implementations /// <summary> /// Creates a new runtime map group /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="group">The group.</param> /// <returns></returns> public virtual RuntimeMapGroup CreateMapGroup(RuntimeMap parent, IMapLayerGroup group) => new RuntimeMapGroup(parent, group); //TODO: Review when we decide to split the implementations /// <summary> /// Creates a new runtime map layer from the specified Layer Definition /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="ldf">The layer definition</param> /// <returns></returns> public virtual RuntimeMapLayer CreateMapLayer(RuntimeMap parent, ILayerDefinition ldf) => CreateMapLayer(parent, ldf, true); //TODO: Review when we decide to split the implementations /// <summary> /// Creates a new runtime map layer from the specified Layer Definition /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="ldf">The layer definition</param> /// <param name="suppressErrors"></param> /// <returns></returns> public virtual RuntimeMapLayer CreateMapLayer(RuntimeMap parent, ILayerDefinition ldf, bool suppressErrors) => new RuntimeMapLayer(parent, ldf, suppressErrors); //TODO: Review when we decide to split the implementations /// <summary> /// Creates a new runtime map layer from the specified <see cref="T:OSGeo.MapGuide.ObjectModels.MapDefinition.IBaseMapLayer"/> instance /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="source">The map definition layer</param> /// <returns></returns> public RuntimeMapLayer CreateMapLayer(RuntimeMap parent, IBaseMapLayer source) => CreateMapLayer(parent, source, true); /// <summary> /// Creates a new runtime map layer from the specified <see cref="T:OSGeo.MapGuide.ObjectModels.MapDefinition.IBaseMapLayer"/> instance /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="source">The map definition layer</param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMapLayer CreateMapLayer(RuntimeMap parent, IBaseMapLayer source, bool suppressErrors) { ILayerDefinition layerDef = (ILayerDefinition)GetResource(source.ResourceId); var rtLayer = CreateMapLayer(parent, layerDef, suppressErrors); //These may not match, so set them here rtLayer.ExpandInLegend = source.ExpandInLegend; rtLayer.LegendLabel = source.LegendLabel; rtLayer.Name = source.Name; rtLayer.Selectable = source.Selectable; rtLayer.ShowInLegend = source.ShowInLegend; rtLayer.Visible = true; rtLayer.Type = RuntimeMapLayer.kBaseMap; return rtLayer; } /// <summary> /// Creates a new runtime map layer from the specified <see cref="T:OSGeo.MapGuide.ObjectModels.MapDefinition.IBaseMapLayer"/> instance /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="source">The map definition layer</param> /// <returns></returns> public RuntimeMapLayer CreateMapLayer(RuntimeMap parent, IMapLayer source) => CreateMapLayer(parent, source); /// <summary> /// Creates a new runtime map layer from the specified <see cref="T:OSGeo.MapGuide.ObjectModels.MapDefinition.IBaseMapLayer"/> instance /// </summary> /// <param name="parent">The parent runtime map. The runtime map must have been created or opened from this same service instance</param> /// <param name="source">The map definition layer</param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMapLayer CreateMapLayer(RuntimeMap parent, IMapLayer source, bool suppressErrors) { ILayerDefinition layerDef = (ILayerDefinition)GetResource(source.ResourceId); var rtLayer = CreateMapLayer(parent, layerDef, suppressErrors); //These may not match, so set them here rtLayer.ExpandInLegend = source.ExpandInLegend; rtLayer.LegendLabel = source.LegendLabel; rtLayer.Name = source.Name; rtLayer.Selectable = source.Selectable; rtLayer.ShowInLegend = source.ShowInLegend; rtLayer.Group = source.Group; rtLayer.Visible = source.Visible; return rtLayer; } /// <summary> /// Creates a new runtime map instance from an existing map definition. Meters per unit /// is calculated from the Coordinate System WKT of the map definition. /// </summary> /// <remarks> /// Calculation of meters-per-unit may differ between implementations. This may have an adverse /// effect on things such as rendering and measuring depending on the underlying implementation /// /// If you are certain of the meters-per-unit value required, use the overloaded method that /// accepts a metersPerUnit parameter. /// </remarks> /// <param name="runtimeMapResourceId"></param> /// <param name="baseMapDefinitionId"></param> /// <returns></returns> public RuntimeMap CreateMap(string runtimeMapResourceId, string baseMapDefinitionId) => CreateMap(runtimeMapResourceId, baseMapDefinitionId, true); /// <summary> /// Creates a new runtime map instance from an existing map definition. Meters per unit /// is calculated from the Coordinate System WKT of the map definition. /// </summary> /// <remarks> /// Calculation of meters-per-unit may differ between implementations. This may have an adverse /// effect on things such as rendering and measuring depending on the underlying implementation /// /// If you are certain of the meters-per-unit value required, use the overloaded method that /// accepts a metersPerUnit parameter. /// </remarks> /// <param name="runtimeMapResourceId"></param> /// <param name="baseMapDefinitionId"></param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMap CreateMap(string runtimeMapResourceId, string baseMapDefinitionId, bool suppressErrors) { var mdf = (IMapDefinition)GetResource(baseMapDefinitionId); double mpu = 1.0; mpu = CsHelper.DefaultCalculator != null ? CsHelper.DefaultCalculator.Calculate(mdf.CoordinateSystem, 1.0) : InferMPU(mdf.CoordinateSystem, 1.0); return CreateMap(runtimeMapResourceId, mdf, mpu, suppressErrors); } /// <summary> /// Creates a new runtime map instance from an existing map definition /// </summary> /// <param name="runtimeMapResourceId"></param> /// <param name="baseMapDefinitionId"></param> /// <param name="metersPerUnit"></param> /// <returns></returns> public virtual RuntimeMap CreateMap(string runtimeMapResourceId, string baseMapDefinitionId, double metersPerUnit) => CreateMap(runtimeMapResourceId, baseMapDefinitionId, metersPerUnit, true); /// <summary> /// Creates a new runtime map instance from an existing map definition /// </summary> /// <param name="runtimeMapResourceId"></param> /// <param name="baseMapDefinitionId"></param> /// <param name="metersPerUnit"></param> /// <param name="suppressErrors"></param> /// <returns></returns> public virtual RuntimeMap CreateMap(string runtimeMapResourceId, string baseMapDefinitionId, double metersPerUnit, bool suppressErrors) { var mdf = (IMapDefinition)GetResource(baseMapDefinitionId); return CreateMap(runtimeMapResourceId, mdf, metersPerUnit, suppressErrors); } /// <summary> /// Creates a new runtime map instance from an existing map definition. The runtime map resource id is calculated from the /// current session id and the name component of the Map Definition resource id /// </summary> /// <param name="mdf"></param> /// <returns></returns> public RuntimeMap CreateMap(IMapDefinition mdf) => CreateMap(mdf, true); /// <summary> /// Creates a new runtime map instance from an existing map definition. The runtime map resource id is calculated from the /// current session id and the name component of the Map Definition resource id /// </summary> /// <param name="mdf"></param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMap CreateMap(IMapDefinition mdf, bool suppressErrors) { var rid = new ResourceIdentifier(ResourceIdentifier.GetName(mdf.ResourceID), ResourceTypes.Map, this.SessionID); return CreateMap(rid.ToString(), mdf, suppressErrors); } /// <summary> /// Creates a new runtime map instance from an existing map definition. The runtime map resource id is calculated from the /// current session id and the name component of the Map Definition resource id /// </summary> /// <param name="mdf">The map definition.</param> /// <param name="metersPerUnit">The meters per unit.</param> /// <returns></returns> public RuntimeMap CreateMap(IMapDefinition mdf, double metersPerUnit) => CreateMap(mdf, metersPerUnit, true); /// <summary> /// Creates a new runtime map instance from an existing map definition. The runtime map resource id is calculated from the /// current session id and the name component of the Map Definition resource id /// </summary> /// <param name="mdf">The map definition.</param> /// <param name="metersPerUnit">The meters per unit.</param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMap CreateMap(IMapDefinition mdf, double metersPerUnit, bool suppressErrors) { var rid = new ResourceIdentifier(ResourceIdentifier.GetName(mdf.ResourceID), ResourceTypes.Map, this.SessionID); return CreateMap(rid.ToString(), mdf, metersPerUnit, suppressErrors); } /// <summary> /// Creates a new runtime map instance from an existing map definition. Meters per unit /// is calculated from the Coordinate System WKT of the map definition. /// </summary> /// <remarks> /// Calculation of meters-per-unit may differ between implementations. This may have an adverse /// effect on things such as rendering and measuring depending on the underlying implementation /// /// If you are certain of the meters-per-unit value required, use the overloaded method that /// accepts a metersPerUnit parameter. /// </remarks> /// <param name="runtimeMapResourceId"></param> /// <param name="mdf"></param> /// <returns></returns> public RuntimeMap CreateMap(string runtimeMapResourceId, IMapDefinition mdf) => CreateMap(runtimeMapResourceId, mdf, true); /// <summary> /// Creates a new runtime map instance from an existing map definition. Meters per unit /// is calculated from the Coordinate System WKT of the map definition. /// </summary> /// <remarks> /// Calculation of meters-per-unit may differ between implementations. This may have an adverse /// effect on things such as rendering and measuring depending on the underlying implementation /// /// If you are certain of the meters-per-unit value required, use the overloaded method that /// accepts a metersPerUnit parameter. /// </remarks> /// <param name="runtimeMapResourceId"></param> /// <param name="mdf"></param> /// <param name="suppressErrors"></param> /// <returns></returns> public RuntimeMap CreateMap(string runtimeMapResourceId, IMapDefinition mdf, bool suppressErrors) { double mpu = 1.0; mpu = CsHelper.DefaultCalculator != null ? CsHelper.DefaultCalculator.Calculate(mdf.CoordinateSystem, 1.0) : InferMPU(mdf.CoordinateSystem, 1.0); return CreateMap(runtimeMapResourceId, mdf, mpu, suppressErrors); } /// <summary> /// Creates a new runtime map instance from an existing map definition /// </summary> /// <param name="runtimeMapResourceId"></param> /// <param name="mdf"></param> /// <param name="metersPerUnit"></param> /// <returns></returns> public virtual RuntimeMap CreateMap(string runtimeMapResourceId, IMapDefinition mdf, double metersPerUnit) => CreateMap(runtimeMapResourceId, mdf, metersPerUnit, true); /// <summary> /// Creates a new runtime map instance from an existing map definition /// </summary> /// <param name="runtimeMapResourceId"></param> /// <param name="mdf"></param> /// <param name="metersPerUnit"></param> /// <param name="suppressErrors"></param> /// <returns></returns> public virtual RuntimeMap CreateMap(string runtimeMapResourceId, IMapDefinition mdf, double metersPerUnit, bool suppressErrors) { var map = new RuntimeMap(GetInterface(), mdf, metersPerUnit, suppressErrors); map.ResourceID = runtimeMapResourceId; map.IsDirty = false; return map; } /// <summary> /// Opens the specified runtime map /// </summary> /// <param name="runtimeMapResourceId"></param> /// <returns></returns> public virtual RuntimeMap OpenMap(string runtimeMapResourceId) { if (!runtimeMapResourceId.StartsWith("Session:") || !runtimeMapResourceId.EndsWith(".Map")) //NOXLATE throw new ArgumentException(Strings.ErrorRuntimeMapNotInSessionRepo); var map = new RuntimeMap(GetInterface()); map.Deserialize(new MgBinaryDeserializer(this.GetResourceData(runtimeMapResourceId, "RuntimeData"), this.SiteVersion)); //NOXLATE if (this.SiteVersion >= SiteVersions.GetVersion(KnownSiteVersions.MapGuideOS1_2)) map.DeserializeLayerData(new MgBinaryDeserializer(this.GetResourceData(runtimeMapResourceId, "LayerGroupData"), this.SiteVersion)); //NOXLATE map.IsDirty = false; return map; } #endregion runtime map #region Load Procedure /// <summary> /// Executes the load procedure. /// </summary> /// <param name="loadProc">The load proc.</param> /// <param name="callback">The callback.</param> /// <param name="ignoreUnsupported">if set to <c>true</c> [ignore unsupported].</param> /// <returns></returns> public virtual string[] ExecuteLoadProcedure(ILoadProcedure loadProc, OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack callback, bool ignoreUnsupported) { var cmd = new ExecuteLoadProcedure(GetInterface()); cmd.IgnoreUnsupportedFeatures = ignoreUnsupported; return cmd.Execute(loadProc, callback); } /// <summary> /// Executes the load procedure. /// </summary> /// <param name="resourceID">The resource ID.</param> /// <param name="callback">The callback.</param> /// <param name="ignoreUnsupported">if set to <c>true</c> [ignore unsupported].</param> /// <returns></returns> public virtual string[] ExecuteLoadProcedure(string resourceID, OSGeo.MapGuide.MaestroAPI.LengthyOperationProgressCallBack callback, bool ignoreUnsupported) { var cmd = new ExecuteLoadProcedure(GetInterface()); cmd.IgnoreUnsupportedFeatures = ignoreUnsupported; return cmd.Execute(resourceID, callback); } #endregion Load Procedure } }