[mapguide-commits] r5045 - in sandbox/maestro-3.0: . Maestro Maestro.Base Maestro.Base/Commands/SiteExplorer Maestro.Base/Properties Maestro.Base/UI Maestro.Editors Maestro.ResourceValidation Maestro.ResourceValidation/Properties Maestro.Shared.UI OSGeo.MapGuide.MaestroAPI OSGeo.MapGuide.MaestroAPI/ObjectModels OSGeo.MapGuide.MaestroAPI/Properties OSGeo.MapGuide.MaestroAPI/Resource OSGeo.MapGuide.MaestroAPI.Http

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Wed Jul 21 06:14:11 EDT 2010


Author: jng
Date: 2010-07-21 10:14:11 +0000 (Wed, 21 Jul 2010)
New Revision: 5045

Added:
   sandbox/maestro-3.0/Maestro.Base/Commands/SiteExplorer/ValidateCommand.cs
   sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.cs
   sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.designer.cs
   sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.resx
   sandbox/maestro-3.0/Maestro.ResourceValidation/
   sandbox/maestro-3.0/Maestro.ResourceValidation/ApplicationDefinitionValidator.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/FeatureSourceValidator.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/LayerDefinitionValidator.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/Maestro.ResourceValidation.csproj
   sandbox/maestro-3.0/Maestro.ResourceValidation/MapDefinitionValidator.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/
   sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/AssemblyInfo.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.Designer.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.resx
   sandbox/maestro-3.0/Maestro.ResourceValidation/ResourceValidatorLoader.cs
   sandbox/maestro-3.0/Maestro.ResourceValidation/WebLayoutValidator.cs
   sandbox/maestro-3.0/Maestro.Shared.UI/CancelException.cs
   sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.Designer.cs
   sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.cs
   sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.resx
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/IResourceValidator.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/ResourceValidatorSet.cs
Modified:
   sandbox/maestro-3.0/Maestro.Base/Maestro.Base.addin
   sandbox/maestro-3.0/Maestro.Base/Maestro.Base.csproj
   sandbox/maestro-3.0/Maestro.Base/Properties/Resources.Designer.cs
   sandbox/maestro-3.0/Maestro.Base/Properties/Resources.resx
   sandbox/maestro-3.0/Maestro.Editors/Maestro.Editors.csproj
   sandbox/maestro-3.0/Maestro.Shared.UI/Maestro.Shared.UI.csproj
   sandbox/maestro-3.0/Maestro/Maestro.csproj
   sandbox/maestro-3.0/Maestro/Maestro.sln
   sandbox/maestro-3.0/Maestro/Program.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/ApplicationDefinition.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/Envelope.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/FeatureSource.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/LayerDefinition.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.Designer.cs
   sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.resx
Log:
This submission includes the port of the resource validation framework from Maestro 2.1
with some changes:
 - Validation is now done on a per-version basis. As a result, attempting to validate something like a LayerDefinition (v1.3.0) without a LayerDefinition 1.3.0 validation will result in the default "no registered validator" warning.
 - Also as a result, some resource validation doesn't currently work as only v1.0.0 validators are included by default.

Also other changes:
 - Fix the output of TestConnection API in the http implementation
 - Add validate context menu command, because we now have a multi-select treeview this can now validate a *selection* of resources.
 - Add more resource object interfaces, and flesh out existing ones. The idea is that although there may be multiple versions of a given resource, we should be able to access commmon properties through a common interface.
 - Add extra APIs to the generated Envelope class.

Modified: sandbox/maestro-3.0/Maestro/Maestro.csproj
===================================================================
--- sandbox/maestro-3.0/Maestro/Maestro.csproj	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro/Maestro.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{E0C36475-2B70-4F6D-ACE0-8943167806DC}</ProjectGuid>
     <OutputType>WinExe</OutputType>
@@ -79,6 +79,10 @@
       <Project>{F1E2F468-5030-4DBA-968C-9620284AFAA1}</Project>
       <Name>Maestro.Base</Name>
     </ProjectReference>
+    <ProjectReference Include="..\Maestro.ResourceValidation\Maestro.ResourceValidation.csproj">
+      <Project>{3B29C138-666C-46D0-A55D-824E5192C091}</Project>
+      <Name>Maestro.ResourceValidation</Name>
+    </ProjectReference>
     <ProjectReference Include="..\OSGeo.MapGuide.MaestroAPI.Http\OSGeo.MapGuide.MaestroAPI.Http.csproj">
       <Project>{6EF1E775-444B-4E5F-87FB-D687C43A68D7}</Project>
       <Name>OSGeo.MapGuide.MaestroAPI.Http</Name>

Modified: sandbox/maestro-3.0/Maestro/Maestro.sln
===================================================================
--- sandbox/maestro-3.0/Maestro/Maestro.sln	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro/Maestro.sln	2010-07-21 10:14:11 UTC (rev 5045)
@@ -66,6 +66,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OSGeo.MapGuide.MaestroAPI.Native-2.0.2", "..\OSGeo.MapGuide.MaestroAPI.Native\OSGeo.MapGuide.MaestroAPI.Native-2.0.2.csproj", "{A6F27323-28A7-4F65-A54A-337B09962C0F}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Maestro.ResourceValidation", "..\Maestro.ResourceValidation\Maestro.ResourceValidation.csproj", "{3B29C138-666C-46D0-A55D-824E5192C091}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -168,6 +170,10 @@
 		{A6F27323-28A7-4F65-A54A-337B09962C0F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{A6F27323-28A7-4F65-A54A-337B09962C0F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{A6F27323-28A7-4F65-A54A-337B09962C0F}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3B29C138-666C-46D0-A55D-824E5192C091}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3B29C138-666C-46D0-A55D-824E5192C091}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3B29C138-666C-46D0-A55D-824E5192C091}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3B29C138-666C-46D0-A55D-824E5192C091}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -185,6 +191,7 @@
 		{CFD19053-2172-41D3-8460-0FD2123A1E88} = {CCF50F7F-DD89-41C1-843C-1BFA2375EEE0}
 		{5AD2CDBA-952E-4148-98A1-31D2E0D540D5} = {CCF50F7F-DD89-41C1-843C-1BFA2375EEE0}
 		{07588440-5F9F-4C30-AA06-9CF30BA6DDE6} = {CCF50F7F-DD89-41C1-843C-1BFA2375EEE0}
+		{3B29C138-666C-46D0-A55D-824E5192C091} = {CCF50F7F-DD89-41C1-843C-1BFA2375EEE0}
 		{E9BDFC13-1555-4050-869C-D2C44B7ED447} = {FCC83861-96C6-43CA-B33B-DB1D0DEC8E79}
 		{AF9E8895-0B12-4265-B3C9-89E319B49E70} = {FCC83861-96C6-43CA-B33B-DB1D0DEC8E79}
 		{F4420153-9DF3-4407-AD65-E8ABED2B6E25} = {FCC83861-96C6-43CA-B33B-DB1D0DEC8E79}

Modified: sandbox/maestro-3.0/Maestro/Program.cs
===================================================================
--- sandbox/maestro-3.0/Maestro/Program.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro/Program.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -28,6 +28,7 @@
 using Maestro.Base.UI;
 using OSGeo.MapGuide.MaestroAPI;
 using ICSharpCode.Core.WinForms;
+using Maestro.ResourceValidation;
 
 namespace Maestro
 {
@@ -61,6 +62,9 @@
                 LoggingService.Info(Properties.Resources.Warn_Mono);
             }
 
+            //Init our default set of resource validators
+            ResourceValidatorLoader.LoadStockValidators();
+
             AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
 
             // Get a reference to the entry assembly (Startup.exe)

Added: sandbox/maestro-3.0/Maestro.Base/Commands/SiteExplorer/ValidateCommand.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/Commands/SiteExplorer/ValidateCommand.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Base/Commands/SiteExplorer/ValidateCommand.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,149 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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 ICSharpCode.Core;
+using Maestro.Base.Services;
+using Maestro.Shared.UI;
+using OSGeo.MapGuide.MaestroAPI.Resource;
+using System.ComponentModel;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+using OSGeo.MapGuide.ObjectModels.Common;
+
+namespace Maestro.Base.Commands.SiteExplorer
+{
+    internal class ValidateCommand : AbstractMenuCommand
+    {
+        private IServerConnection _conn;
+
+        public override void Run()
+        {
+            var wb = Workbench.Instance;
+            var items = wb.ActiveSiteExplorer.SelectedItems;
+            var connMgr = ServiceRegistry.GetService<ServerConnectionManager>();
+            _conn = connMgr.GetConnection(wb.ActiveSiteExplorer.ConnectionName);
+
+            if (items.Length > 0)
+            {
+                var pdlg = new ProgressDialog();
+                pdlg.CancelAbortsThread = true;
+
+                string[] args = new string[items.Length];
+                for (int i = 0; i < items.Length; i++)
+                {
+                    args[i] = items[i].ResourceId;
+                }
+
+                var issues = (ValidationIssue[])pdlg.RunOperationAsync(wb, new ProgressDialog.DoBackgroundWork(BackgroundValidate), items[0].ResourceId);
+
+                if (issues != null)
+                {
+                    if (issues.Length > 0)
+                    {
+                        //Sigh! LINQ would've made this code so simple...
+
+                        var sort = new Dictionary<string, List<ValidationIssue>>();
+                        foreach (var issue in issues)
+                        {
+                            string resId = issue.Resource.ResourceID;
+                            if (!sort.ContainsKey(resId))
+                                sort[resId] = new List<ValidationIssue>();
+
+                            sort[resId].Add(issue);
+                        }
+
+                        var groupedIssues = new List<KeyValuePair<string, ValidationIssue[]>>();
+                        foreach (var kvp in sort)
+                        {
+                            groupedIssues.Add(
+                                new KeyValuePair<string, ValidationIssue[]>(
+                                    kvp.Key,
+                                    kvp.Value.ToArray()));
+                        }
+
+                        var resDlg = new ValidationResultsDialog(groupedIssues);
+                        resDlg.ShowDialog(wb);
+                    }
+                    else
+                    {
+                        MessageService.ShowMessage(Properties.Resources.ValidationNoIssues);
+                    }
+                }
+            }
+        }
+
+        private object BackgroundValidate(BackgroundWorker worker, DoWorkEventArgs e, params object[] args)
+        {
+            //Collect all documents to be validated. Some of these selected items
+            //may be folders.
+            var documents = new List<string>();
+            foreach (object a in args)
+            {
+                string rid = a.ToString();
+                if (ResourceIdentifier.Validate(rid))
+                {
+                    var resId = new ResourceIdentifier(rid);
+                    if (resId.IsFolder)
+                    {
+                        foreach (IRepositoryItem o in _conn.ResourceService.GetRepositoryResources((string)args[0]).Children)
+                        {
+                            if (!o.IsFolder)
+                            {
+                                documents.Add(o.ResourceId);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        documents.Add(rid);
+                    }
+                }
+            }
+
+            worker.ReportProgress(0);
+
+            var issues = new List<ValidationIssue>();
+            int i = 0;
+            foreach (string s in documents)
+            {
+                worker.ReportProgress((int)((i / (double)documents.Count) * 100), s);
+                IResource item = null;
+                try
+                {
+                    //TODO: This will validate resources multiple times, if they are referenced by
+                    //resources inside the folder
+                    item = _conn.ResourceService.GetResource(s);
+                    issues.AddRange(ResourceValidatorSet.Validate(item, true));
+                }
+                catch (Exception ex)
+                {
+                    string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                    issues.Add(new ValidationIssue(item, ValidationStatus.Error, string.Format(Properties.Resources.ValidationResourceLoadFailed, msg)));
+                }
+                i++;
+                worker.ReportProgress((int)((i / (double)documents.Count) * 100), s);
+            }
+
+            return issues.ToArray();
+        }
+    }
+}

Modified: sandbox/maestro-3.0/Maestro.Base/Maestro.Base.addin
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/Maestro.Base.addin	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Base/Maestro.Base.addin	2010-07-21 10:14:11 UTC (rev 5045)
@@ -285,6 +285,9 @@
             <MenuItem id="Paste"
                       label="${res:SiteExplorer_SelectedFolder_Paste}"
                       class="Maestro.Base.Commands.NotImplementedCommand" />
+            <MenuItem id="Validate"
+                      label="${res:SiteExplorer_ValidateResources}"
+                      class="Maestro.Base.Commands.SiteExplorer.ValidateCommand" />
         </Condition>
     </Path>
 
@@ -304,6 +307,9 @@
             <MenuItem id="Cut"
                       label="${res:SiteExplorer_SelectedItem_Cut}"
                       class="Maestro.Base.Commands.NotImplementedCommand" />
+            <MenuItem id="Validate"
+                      label="${res:SiteExplorer_ValidateResources}"
+                      class="Maestro.Base.Commands.SiteExplorer.ValidateCommand" />
         </Condition>
     </Path>
 
@@ -329,6 +335,9 @@
             <MenuItem id="Paste"
                       label="${res:SiteExplorer_SelectedFolder_Paste}"
                       class="Maestro.Base.Commands.NotImplementedCommand" />
+            <MenuItem id="Validate"
+                      label="${res:SiteExplorer_ValidateResources}"
+                      class="Maestro.Base.Commands.SiteExplorer.ValidateCommand" />
             <MenuItem type="Separator" />
             <MenuItem id="Properties"
                       label="${res:SiteExplorer_SelectedItem_Properties}"
@@ -352,6 +361,9 @@
             <MenuItem id="Cut"
                       label="${res:SiteExplorer_SelectedItem_Cut}"
                       class="Maestro.Base.Commands.NotImplementedCommand" />
+            <MenuItem id="Validate"
+                      label="${res:SiteExplorer_ValidateResources}"
+                      class="Maestro.Base.Commands.SiteExplorer.ValidateCommand" />
         </Condition>
     </Path>
 
@@ -375,6 +387,9 @@
             <MenuItem id="Cut"
                       label="${res:SiteExplorer_SelectedItem_Cut}"
                       class="Maestro.Base.Commands.NotImplementedCommand" />
+            <MenuItem id="Validate"
+                      label="${res:SiteExplorer_ValidateResources}"
+                      class="Maestro.Base.Commands.SiteExplorer.ValidateCommand" />
         </Condition>
     </Path>
     

Modified: sandbox/maestro-3.0/Maestro.Base/Maestro.Base.csproj
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/Maestro.Base.csproj	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Base/Maestro.Base.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -66,6 +66,7 @@
     <Compile Include="Commands\SiteExplorer\OpenResourceCommand.cs" />
     <Compile Include="Commands\SiteExplorer\RefreshCommand.cs" />
     <Compile Include="Commands\SiteExplorer\ResourcePropertiesCommand.cs" />
+    <Compile Include="Commands\SiteExplorer\ValidateCommand.cs" />
     <Compile Include="Commands\StartupCommand.cs" />
     <Compile Include="Commands\Test\OpenCoordinateSystemPickerCommand.cs" />
     <Compile Include="Commands\Test\OpenResourceCommand.cs" />
@@ -216,6 +217,12 @@
       <DependentUpon>SiteExplorer.cs</DependentUpon>
     </Compile>
     <Compile Include="TabFactory.cs" />
+    <Compile Include="UI\ValidationResultsDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="UI\ValidationResultsDialog.designer.cs">
+      <DependentUpon>ValidationResultsDialog.cs</DependentUpon>
+    </Compile>
     <Compile Include="UI\WelcomeScreen.cs">
       <SubType>UserControl</SubType>
     </Compile>
@@ -352,6 +359,9 @@
       <DependentUpon>SiteExplorer.cs</DependentUpon>
       <SubType>Designer</SubType>
     </EmbeddedResource>
+    <EmbeddedResource Include="UI\ValidationResultsDialog.resx">
+      <DependentUpon>ValidationResultsDialog.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="UI\WelcomeScreen.resx">
       <DependentUpon>WelcomeScreen.cs</DependentUpon>
       <SubType>Designer</SubType>

Modified: sandbox/maestro-3.0/Maestro.Base/Properties/Resources.Designer.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/Properties/Resources.Designer.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Base/Properties/Resources.Designer.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -1040,6 +1040,15 @@
             }
         }
         
+        /// <summary>
+        ///   Looks up a localized string similar to Validate.
+        /// </summary>
+        internal static string SiteExplorer_ValidateResources {
+            get {
+                return ResourceManager.GetString("SiteExplorer_ValidateResources", resourceCulture);
+            }
+        }
+        
         internal static System.Drawing.Bitmap tick {
             get {
                 object obj = ResourceManager.GetObject("tick", resourceCulture);
@@ -1271,5 +1280,68 @@
                 return ResourceManager.GetString("TPL_WL_NAME", resourceCulture);
             }
         }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Building resource list....
+        /// </summary>
+        internal static string ValidationBuildingList {
+            get {
+                return ResourceManager.GetString("ValidationBuildingList", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to save file: {0}.
+        /// </summary>
+        internal static string ValidationFailedError {
+            get {
+                return ResourceManager.GetString("ValidationFailedError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No issues found during validation.
+        /// </summary>
+        internal static string ValidationNoIssues {
+            get {
+                return ResourceManager.GetString("ValidationNoIssues", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Validating resource: {0}.
+        /// </summary>
+        internal static string ValidationProgressMessage {
+            get {
+                return ResourceManager.GetString("ValidationProgressMessage", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to load resource: {0}.
+        /// </summary>
+        internal static string ValidationResourceLoadFailed {
+            get {
+                return ResourceManager.GetString("ValidationResourceLoadFailed", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} - {1}: {2}.
+        /// </summary>
+        internal static string ValidationResultFormat {
+            get {
+                return ResourceManager.GetString("ValidationResultFormat", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Validating ....
+        /// </summary>
+        internal static string ValidationValidating {
+            get {
+                return ResourceManager.GetString("ValidationValidating", resourceCulture);
+            }
+        }
     }
 }

Modified: sandbox/maestro-3.0/Maestro.Base/Properties/Resources.resx
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/Properties/Resources.resx	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Base/Properties/Resources.resx	2010-07-21 10:14:11 UTC (rev 5045)
@@ -580,4 +580,37 @@
   <data name="TPL_CSD_NAME" xml:space="preserve">
     <value>Compound Symbol Definition</value>
   </data>
+  <data name="ValidationBuildingList" xml:space="preserve">
+    <value>Building resource list...</value>
+    <comment>A message that is displayed while building the list of resources to validate</comment>
+  </data>
+  <data name="ValidationResourceLoadFailed" xml:space="preserve">
+    <value>Failed to load resource: {0}</value>
+    <comment>An error message that is displayed if an error occurs while validating the resource</comment>
+  </data>
+  <data name="SiteExplorer_ValidateResources" xml:space="preserve">
+    <value>Validate</value>
+  </data>
+  <data name="ValidationValidating" xml:space="preserve">
+    <value>Validating ...</value>
+    <comment>A message that is displayed while the validator runs</comment>
+  </data>
+  <data name="ValidationFailedError" xml:space="preserve">
+    <value>Failed to save file: {0}</value>
+    <comment>An error message that is displayed if the log was not saved</comment>
+  </data>
+  <data name="ValidationProgressMessage" xml:space="preserve">
+    <value>Validating resource: {0}</value>
+    <comment>The header text for a resource in the validation log</comment>
+  </data>
+  <data name="ValidationResultFormat" xml:space="preserve">
+    <value>{0} - {1}: {2}</value>
+    <comment>The format used to write the results into the log file.
+{0} is the status, eg. warning
+{1} is the resource id
+{2} is the validation message</comment>
+  </data>
+  <data name="ValidationNoIssues" xml:space="preserve">
+    <value>No issues found during validation</value>
+  </data>
 </root>
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,123 @@
+#region Disclaimer / License
+// Copyright (C) 2009, Kenneth Skovhede
+// http://www.hexad.dk, opensource at 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
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.MaestroAPI.Resource;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+
+namespace Maestro.Base
+{
+    public partial class ValidationResultsDialog : Form
+    {
+        private List<KeyValuePair<string, ValidationIssue[]>> m_issues = new List<KeyValuePair<string, ValidationIssue[]>>();
+
+        public ValidationResultsDialog(string resourceId, ValidationIssue[] issues)
+            : this(
+                new List<KeyValuePair<string, ValidationIssue[]>>(
+                    new KeyValuePair<string, ValidationIssue[]>[] { 
+                        new KeyValuePair<string, ValidationIssue[]>(resourceId, issues) 
+                    }
+                )
+            )
+        { }
+
+        public ValidationResultsDialog(List<KeyValuePair<string, ValidationIssue[]>> issues)
+            : this()
+        {
+            listView1.Items.Clear();
+
+            m_issues = issues;
+
+            foreach (KeyValuePair<string, ValidationIssue[]> e in issues)
+            {
+                foreach (ValidationIssue issue in e.Value)
+                {
+                    if (issue == null || issue.Resource == null || string.IsNullOrEmpty(issue.Message) || string.IsNullOrEmpty(issue.Resource.ResourceID))
+                        continue;
+
+                    ListViewItem lvi = new ListViewItem(issue.Resource.ResourceID);
+                    switch (issue.Status)
+                    {
+                        case ValidationStatus.Information:
+                            lvi.ImageIndex = 0;
+                            break;
+                        case ValidationStatus.Warning:
+                            lvi.ImageIndex = 1;
+                            break;
+                        case ValidationStatus.Error:
+                            lvi.ImageIndex = 2;
+                            break;
+                        default:
+                            lvi.ImageIndex = -1;
+                            break;
+                    }
+                    lvi.SubItems.Add(issue.Message);
+                    listView1.Items.Add(lvi);
+                }
+            }
+        }
+
+        private ValidationResultsDialog()
+        {
+            InitializeComponent();
+        }
+
+        private void CancelBtn_Click(object sender, EventArgs e)
+        {
+            this.Close();
+        }
+
+        private void SaveReportBtn_Click(object sender, EventArgs e)
+        {
+            try
+            {
+                if (saveFileDialog.ShowDialog(this) == DialogResult.OK)
+                {
+                    using (System.IO.StreamWriter sw = new System.IO.StreamWriter(saveFileDialog.FileName, false))
+                    {
+                        foreach (KeyValuePair<string, ValidationIssue[]> p in m_issues)
+                        {
+                            if (p.Value.Length > 0)
+                            {
+                                sw.WriteLine(new string('*', 80));
+                                sw.WriteLine(string.Format(Properties.Resources.ValidationProgressMessage, p.Key));
+                                foreach (ValidationIssue i in p.Value)
+                                    sw.WriteLine(string.Format(Properties.Resources.ValidationResultFormat, i.Status, i.Resource, i.Message));
+
+                                sw.WriteLine();
+                            }
+                        }
+                    }
+                }
+            }
+            catch (Exception ex)
+            {
+                string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                MessageBox.Show(this, string.Format(Properties.Resources.ValidationFailedError, msg), Application.ProductName, MessageBoxButtons.OK, MessageBoxIcon.Error);
+            }
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.designer.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.designer.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.designer.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,124 @@
+namespace Maestro.Base
+{
+    partial class ValidationResultsDialog
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.components = new System.ComponentModel.Container();
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ValidationResultsDialog));
+            this.panel1 = new System.Windows.Forms.Panel();
+            this.SaveReportBtn = new System.Windows.Forms.Button();
+            this.CancelBtn = new System.Windows.Forms.Button();
+            this.listView1 = new System.Windows.Forms.ListView();
+            this.columnHeader1 = new System.Windows.Forms.ColumnHeader();
+            this.columnHeader2 = new System.Windows.Forms.ColumnHeader();
+            this.imageList1 = new System.Windows.Forms.ImageList(this.components);
+            this.saveFileDialog = new System.Windows.Forms.SaveFileDialog();
+            this.panel1.SuspendLayout();
+            this.SuspendLayout();
+            // 
+            // panel1
+            // 
+            this.panel1.Controls.Add(this.SaveReportBtn);
+            this.panel1.Controls.Add(this.CancelBtn);
+            resources.ApplyResources(this.panel1, "panel1");
+            this.panel1.Name = "panel1";
+            // 
+            // SaveReportBtn
+            // 
+            resources.ApplyResources(this.SaveReportBtn, "SaveReportBtn");
+            this.SaveReportBtn.Name = "SaveReportBtn";
+            this.SaveReportBtn.UseVisualStyleBackColor = true;
+            this.SaveReportBtn.Click += new System.EventHandler(this.SaveReportBtn_Click);
+            // 
+            // CancelBtn
+            // 
+            resources.ApplyResources(this.CancelBtn, "CancelBtn");
+            this.CancelBtn.DialogResult = System.Windows.Forms.DialogResult.Cancel;
+            this.CancelBtn.Name = "CancelBtn";
+            this.CancelBtn.UseVisualStyleBackColor = true;
+            this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
+            // 
+            // listView1
+            // 
+            this.listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
+            this.columnHeader1,
+            this.columnHeader2});
+            resources.ApplyResources(this.listView1, "listView1");
+            this.listView1.GridLines = true;
+            this.listView1.Name = "listView1";
+            this.listView1.SmallImageList = this.imageList1;
+            this.listView1.UseCompatibleStateImageBehavior = false;
+            this.listView1.View = System.Windows.Forms.View.Details;
+            // 
+            // columnHeader1
+            // 
+            resources.ApplyResources(this.columnHeader1, "columnHeader1");
+            // 
+            // columnHeader2
+            // 
+            resources.ApplyResources(this.columnHeader2, "columnHeader2");
+            // 
+            // imageList1
+            // 
+            this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
+            this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
+            this.imageList1.Images.SetKeyName(0, "Information.ico");
+            this.imageList1.Images.SetKeyName(1, "Warning.ico");
+            this.imageList1.Images.SetKeyName(2, "Error.ico");
+            // 
+            // saveFileDialog1
+            // 
+            this.saveFileDialog.DefaultExt = "txt";
+            resources.ApplyResources(this.saveFileDialog, "saveFileDialog1");
+            // 
+            // ValidationResults
+            // 
+            resources.ApplyResources(this, "$this");
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.CancelButton = this.CancelBtn;
+            this.Controls.Add(this.listView1);
+            this.Controls.Add(this.panel1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow;
+            this.Name = "ValidationResults";
+            this.panel1.ResumeLayout(false);
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Panel panel1;
+        private System.Windows.Forms.Button CancelBtn;
+        private System.Windows.Forms.ListView listView1;
+        private System.Windows.Forms.ColumnHeader columnHeader1;
+        private System.Windows.Forms.ColumnHeader columnHeader2;
+        private System.Windows.Forms.ImageList imageList1;
+        private System.Windows.Forms.Button SaveReportBtn;
+        private System.Windows.Forms.SaveFileDialog saveFileDialog;
+    }
+}
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.resx
===================================================================
--- sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.resx	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Base/UI/ValidationResultsDialog.resx	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,365 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="SaveReportBtn.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
+    <value>Bottom, Right</value>
+  </data>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="SaveReportBtn.Location" type="System.Drawing.Point, System.Drawing">
+    <value>488, 8</value>
+  </data>
+  <data name="SaveReportBtn.Size" type="System.Drawing.Size, System.Drawing">
+    <value>91, 23</value>
+  </data>
+  <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="SaveReportBtn.TabIndex" type="System.Int32, mscorlib">
+    <value>1</value>
+  </data>
+  <data name="SaveReportBtn.Text" xml:space="preserve">
+    <value>Save as file...</value>
+  </data>
+  <data name="&gt;&gt;SaveReportBtn.Name" xml:space="preserve">
+    <value>SaveReportBtn</value>
+  </data>
+  <data name="&gt;&gt;SaveReportBtn.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;SaveReportBtn.Parent" xml:space="preserve">
+    <value>panel1</value>
+  </data>
+  <data name="&gt;&gt;SaveReportBtn.ZOrder" xml:space="preserve">
+    <value>0</value>
+  </data>
+  <data name="CancelBtn.Anchor" type="System.Windows.Forms.AnchorStyles, System.Windows.Forms">
+    <value>Bottom</value>
+  </data>
+  <data name="CancelBtn.Location" type="System.Drawing.Point, System.Drawing">
+    <value>256, 8</value>
+  </data>
+  <data name="CancelBtn.Size" type="System.Drawing.Size, System.Drawing">
+    <value>75, 23</value>
+  </data>
+  <data name="CancelBtn.TabIndex" type="System.Int32, mscorlib">
+    <value>0</value>
+  </data>
+  <data name="CancelBtn.Text" xml:space="preserve">
+    <value>Close</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Name" xml:space="preserve">
+    <value>CancelBtn</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Parent" xml:space="preserve">
+    <value>panel1</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.ZOrder" xml:space="preserve">
+    <value>1</value>
+  </data>
+  <data name="panel1.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
+    <value>Bottom</value>
+  </data>
+  <data name="panel1.Location" type="System.Drawing.Point, System.Drawing">
+    <value>0, 232</value>
+  </data>
+  <data name="panel1.Size" type="System.Drawing.Size, System.Drawing">
+    <value>595, 42</value>
+  </data>
+  <data name="panel1.TabIndex" type="System.Int32, mscorlib">
+    <value>0</value>
+  </data>
+  <data name="&gt;&gt;panel1.Name" xml:space="preserve">
+    <value>panel1</value>
+  </data>
+  <data name="&gt;&gt;panel1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Panel, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;panel1.Parent" xml:space="preserve">
+    <value>$this</value>
+  </data>
+  <data name="&gt;&gt;panel1.ZOrder" xml:space="preserve">
+    <value>1</value>
+  </data>
+  <data name="columnHeader1.Text" xml:space="preserve">
+    <value>Resource</value>
+  </data>
+  <data name="columnHeader1.Width" type="System.Int32, mscorlib">
+    <value>240</value>
+  </data>
+  <data name="columnHeader2.Text" xml:space="preserve">
+    <value>Message</value>
+  </data>
+  <data name="columnHeader2.Width" type="System.Int32, mscorlib">
+    <value>324</value>
+  </data>
+  <data name="listView1.Dock" type="System.Windows.Forms.DockStyle, System.Windows.Forms">
+    <value>Fill</value>
+  </data>
+  <data name="listView1.Location" type="System.Drawing.Point, System.Drawing">
+    <value>0, 0</value>
+  </data>
+  <data name="listView1.Size" type="System.Drawing.Size, System.Drawing">
+    <value>595, 232</value>
+  </data>
+  <metadata name="imageList1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <data name="imageList1.ImageStream" mimetype="application/x-microsoft.net.object.binary.base64">
+    <value>
+        AAEAAAD/////AQAAAAAAAAAMAgAAAFdTeXN0ZW0uV2luZG93cy5Gb3JtcywgVmVyc2lvbj0yLjAuMC4w
+        LCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2VuPWI3N2E1YzU2MTkzNGUwODkFAQAAACZTeXN0
+        ZW0uV2luZG93cy5Gb3Jtcy5JbWFnZUxpc3RTdHJlYW1lcgEAAAAERGF0YQcCAgAAAAkDAAAADwMAAADy
+        DgAAAk1TRnQBSQFMAgEBAwEAAQwBAAEMAQABEAEAARABAAT/ASEBAAj/AUIBTQE2BwABNgMAASgDAAFA
+        AwABEAMAAQEBAAEgBgABEC4AAwwBEAMVAR0kAAEQAg8BFQMRARcDEQEXAxEBFwMRARcDEQEXAxEBFwMR
+        ARcDEQEXAxEBFwMRARcDFQEdHAADCQEMAxYBHwMWAR8DFgEfAxYBHwMWAR9kAAMSARgBgAEsARoB9wHE
+        AUcBLgH/Ac0BSwExAf8BzgFLATEB/wHKAUoBMAH/AaQBOwEmAf8BSQI0AYYUAAFNATMBNgHNAQIBuwHr
+        Af8BAgG9AewB/wECAcEB7gH/AQIBxAHwAf8BAgHFAfAB/wECAcUB8AH/AQIBxQHwAf8BAgHFAfAB/wEC
+        AcUB8AH/AQIBwwHvAf8BAgG/Ae0B/wECAbsB6wH/AQYBlwG+Af8UAAFSATABMwGrASoBPAG2Af8BLAE/
+        AcAB/wEtAUEBxgH/AS0BQQHHAf8BLAFAAcMB/wErAT4BuwH/ASMBKgGBAf9cAAFKAjQBigHGAUgBLwH/
+        AdcBTgEzAf8B4wFSATYB/wHpAVQBNwH/AeoBVAE3Af8B5gFTATYB/wHdAVABNAH/Ac8BSwExAf8BhQEr
+        ASIB/BAAAQgBggGjAf8BAgG8AewB/wECAcUB8AH/AQIBywHzAf8BAgHOAfUB/wECAdAB9gH/AQIB0AH2
+        Af8BAgHQAfYB/wECAdAB9gH/AQIBzwH2Af8BAgHNAfQB/wECAcgB8gH/AQIBwAHuAf8BAgG7AesB/wMS
+        ARgMAAFSATABMwGrASwBQAHBAf8BLwFEAdAB/wExAUcB2QH/ATIBSQHeAf8BMgFJAd8B/wEyAUgB3AH/
+        ATABRgHVAf8BLQFCAcgB/wEkASwBhAH/VAABOwIyAWMBygFJATAB/wHfAVEBNQH/Ae8BVgE5Af8B+wHx
+        Ae8B/wH9AvkB/wHhAWcBUAH/AfwBWwE8Af8B9QFYAToB/wHnAVMBNwH/AdMBTQEyAf8BhQErASIB/AwA
+        AT4CMwFrAQIBvwHtAf8BAgHJAfIB/wECAdAB9gH/AQIB1AH5Af8BAgHUAfcB/wGFAdwB6wH/AYEB2wHq
+        Af8BAgHQAfMB/wECAdYB+gH/AQIB0gH4Af8BAgHNAfQB/wECAcMB7wH/AQkBgAGdAf8MAAFSATABMwGr
+        ASwBQAHDAf8BMQFGAdYB/wE0AUsB5AH/ATUBTQHsAf8BNgFOAe8B/wE2AU8B8AH/ATYBTgHuAf8BNAFM
+        AeYB/wEyAUkB3QH/AS4BQwHMAf8BJAEsAYQB/1AAAbcBQgErAf8B3QFQATQB/wHyAVcBOQH/Af4BXgFA
+        Cf8B4wGJAXkB/wHoAZsBjQL/AWIBRAH/AfkBWgE7Af8B5wFTATcB/wHPAUsBMQH/AUkCNAGGDAABEgGO
+        Aa4B/wE9AdYB9QH/AUMB3QH5Af8BOQHgAfsB/wEeAc0B6Qn/ASwBygHjAf8BAgHZAfsB/wECAdQB+QH/
+        AQIBzQH1Af8BAgHDAe8B/wEvAisBSQgAAVABMQEzAZ8BKwE/Ab0B/wEwAUYB1QH/ATQBTAHmAf8BqgGz
+        AesB/wGcAaYB6QH/ATkBUgH6Af8BOQFSAfsB/wFZAWsB4AH/AeUB6AH4Af8BNQFMAegB/wEyAUkB3QH/
+        AS0BQgHIAf8BIwEqAYEB/0gAAUcCNAGBAdEBTAEyAf8B6wFUATgB/wH+AVwBPgL/AWgBSwH/AewBsQGl
+        Bf8B8wHMAcQB/wHhAXIBWwH/AfwBagFOAv8BYgFEAf8B9QFYAToB/wHdAVABNAH/AaQBOwEmAf8MAAE9
+        AjMBaAFLAdYB9QH/AVIB3gH5Af8BWAHkAfsB/wFfAegB/QH/AXQB2QHpAf8BgQHeAesB/wELAdsB+wH/
+        AQIB2AH7Af8BAgHTAfgB/wECAcoB8wH/AQkBgQGeAf8MAAEkASwBgwH/AS4BQgHKAf8BMwFKAeAB/wGI
+        AZQB4gn/AZwBpwHsAf8BWgFtAeQJ/wHeAeEB9wH/ATQBTAHoAf8BMAFGAdUB/wErAT4BuwH/AxYBH0QA
+        AYMBJwEfAfsB2gFPATQB/wH0AVcBOQL/AWYBSgL/AXUBWgH/AewBcAFXCf8B+AF2AVsC/wF3AVwC/wFv
+        AVQB/wH8AVsBPAH/AeYBUwE2Af8BygFKATAB/xAAATABlgGwAf8BYAHeAfgB/wFnAeUB+wH/AW4B6QH9
+        Cf8BgQHoAfkB/wFYAeQB+wH/AQIBzwH2Af8BAgHFAfAB/wEuAioBRwwAASUBLgGJAf8BOAFNAdQB/wFG
+        AVwB6QH/AVIBZwH1Af8B1wHbAfcR/wH3AfgB/QH/ATYBTwHwAf8BNgFOAe4B/wEyAUgB3AH/ASwBQAHD
+        Af8DFgEfRAABngE4ASUB/wHfAVEBNQH/AfcBZwFLAv8BeQFgAf8B+AGAAWcB/wHsAXwBZQH/AfwB8wHx
+        Bf8B5gGLAXsC/wGBAWoC/wF9AWUB/wH+AXIBWAH/AeoBVAE3Af8BzgFLATEB/wMVAR0MAAE8AjMBZgFv
+        Ad4B9wH/AXYB5QH6Af8BfQHqAfwJ/wGPAeoB+QH/AZYB7QH8Af8BXQHdAfcB/wEJAYEBngH/EAABMQE5
+        AY8B/wFMAV4B2wH/AVUBaQHtAf8BXgFzAfgB/wFmAXkB/AH/Ad8B4gH5Cf8B/AH9Af4B/wGFAZQB+QH/
+        AXMBgwH8Af8BNgFPAfAB/wEyAUkB3wH/AS0BQQHHAf8DFgEfRAABlwE2ASMB/wHeAVEBNQH/AfgBgAFn
+        Av8BhQFxAf8B/gGNAXkB/wH1AdgB0gH/AecBmwGNBf8B+QHnAeQC/wGOAXoC/wGJAXUB/wH+AYEBagH/
+        Ae0BdgFfAf8BzQFLATEB/wMMARAQAAFAAZoBsQH/AYMB5gH5Af8BigHrAfwJ/wGeAewB+QH/AaQB7gH7
+        Af8BqgHsAfoB/wEtAioBRhAAATYBPQGQAf8BVgFoAdwB/wFgAXMB7QH/AWkBfAH4Af8BhgGTAeoR/wHJ
+        Ac8B9AH/AZkBpgH8Af8BoAGrAfcB/wFYAWsB5AH/AS0BQQHGAf8DFgEfRAABZQEnASMB1QHcAWgBUQH/
+        AfUBjQF8Av8BkwGBAv8BmwGIAf8B/gGeAYoB/wHtAbEBpgH/AfwB9gH0Af8B7wG8AbIC/wGdAYoC/wGX
+        AYQB/wH7AY8BfQH/AewBigF5Af8BxAFHAS4B/xQAATsCMgFlAZIB5gH5Af8BmQHsAfsJ/wGsAe4B+QH/
+        AbIB7wH7Af8BRAGOAaIB/xQAATsBQQGPAf8BYAFwAdoB/wFrAXwB6wH/AYkBlQHmCf8B+gH7Af4B/wHk
+        AecB+gn/AdMB1wH1Af8BrAG1AfcB/wGxAbkB8QH/AWMBcQHPAf8DFgEfRAABHwIeAS0B1AFrAVYB/wHx
+        AZ4BjgH/AfwBogGRAv8BpwGXAv8BqwGbAv8BrQGdAf8B7wG2AasB/wH+Af0B/AH/AfEBpQGXAf8B/gGk
+        AZMB/wH2AZ8BjwH/AekBmwGMAf8BgAEsARoB9xgAAVABnQGyAf8BpwHtAfsJ/wG5Ae8B+QH/Ab4B8AH7
+        Af8BLQIpAUUUAAEyATUBegH/AWsBeQHWAf8BdgGDAecB/wF+AYoB6QH/A/4B/wL7Af4B/wGVAaIB+QH/
+        AZ0BqQH9Af8B6QHrAfoF/wG2Ab0B8AH/AbcBvwH1Af8BvAHDAfAB/wHBAcYB6gH/AwkBDEgAAYQBLQEZ
+        AfkB7AGuAaIB/wH1AbEBpAH/AfwBtAGmAv8BtgGpAv8BuAGrAf8B9wHdAdkF/wH1AdUBzwH/AfkBsgGl
+        Af8B8AGwAaMB/wHeAZgBigH/AxIBGBgAATsCMgFkAbQB7QH6Cf8BxQHwAfoB/wFJAY8BogH/GAADCAEL
+        AVkBYQGqAf8BgQGLAeEB/wGIAZYB7QH/AZABnAHtAf8BmQGkAfQB/wGhAa0B+wH/AagBswH7Af8BrwG5
+        AfoB/wG5AcAB8QH/AbwBwwH3Af8BwQHIAfMB/wHHAcwB7gH/AVMBMAEzAatPAAEBAb4BeQFrAf8B8AHB
+        AbcB/wH2AcIBuQH/AfoBxAG6Af8B/AHEAboB/wH7AcQBugH/AfIBxgG9Af8B9gHCAbkB/wHzAcIBuAH/
+        AewBvgG1Af8BSgI0AYogAAFdAZ8BsgH/AcYB8QH7Af8BywHyAfwB/wHQAfMB/AH/ASwCKQFEHAADCAEL
+        AWgBbwGwAf8BkwGeAeYB/wGcAacB7gH/AaUBrwHzAf8BrAG2AfYB/wGzAbwB9wH/AboBwgH3Af8BwAHH
+        AfUB/wHGAcwB8wH/AcsB0AHwAf8BUwEwATMBq1cAAQEBjAE+ATYB+QHyAdAByQH/AfQB0QHKAf8B9gHS
+        AcoB/wH2AdIBygH/AfUB0QHKAf8B8wHQAcoB/wHUAZ4BkwH/ATsCMgFjJAABOwIyAWQB0QHyAfsB/wHV
+        AfQB/AH/AU8BkAGjAf8kAAMIAQsBdgF7AbMB/wGnAa8B5wH/Aa8BtwHtAf8BtgG+AfAB/wG9AcQB8gH/
+        AcQBygHyAf8BygHPAfEB/wHPAdMB8AH/AVMBMAEzAatgAAEfAh4BLQFmAScBJAHVAakBbQFiAf8BtAF9
+        AXIB/wGMAUYBNQH7AUcCNAGBMAABSQE3AUAB3AFRAZEBpAH/ARYCFQEeKAADCAELAUoBSwGBAf8BbQFv
+        AZ8B/wFwAXMBogH/AXQBdgGkAf8BdwF4AaQB/wF1AXYBoAH/AVABMgEzAZ//AFEAAUIBTQE+BwABPgMA
+        ASgDAAFAAwABEAMAAQEBAAEBBQABgBcAA/8BAAH+AX8BwAEDAfgBHwIAAfABDwGAAQEB8AEPAgAB4AEH
+        AYABAAHgAQcCAAHAAQMBgAEBAcABAwIAAcABAQHAAQEBgAEBAgABgAEBAcABAwGAAwABgAEBAeABAwGA
+        AwABgAEAAeABBwGAAwABgAEAAfABBwGAAwABgAEBAfABDwGAAwABgAEBAfgBDwGAAwABwAEBAfgBHwGA
+        AQECAAHAAQMB/AEfAcABAwIAAeABBwH8AT8B4AEHAgAB+AEfAf4BPwHwAQ8CAAb/AgAL
+</value>
+  </data>
+  <data name="listView1.TabIndex" type="System.Int32, mscorlib">
+    <value>1</value>
+  </data>
+  <data name="&gt;&gt;listView1.Name" xml:space="preserve">
+    <value>listView1</value>
+  </data>
+  <data name="&gt;&gt;listView1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.ListView, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;listView1.Parent" xml:space="preserve">
+    <value>$this</value>
+  </data>
+  <data name="&gt;&gt;listView1.ZOrder" xml:space="preserve">
+    <value>0</value>
+  </data>
+  <metadata name="saveFileDialog1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>122, 17</value>
+  </metadata>
+  <data name="saveFileDialog1.Filter" xml:space="preserve">
+    <value>Text files (*.txt)|*.txt|All files (*.*)|*.*</value>
+  </data>
+  <data name="saveFileDialog1.Title" xml:space="preserve">
+    <value>Select the filename to write the report to</value>
+  </data>
+  <metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
+    <value>6, 13</value>
+  </data>
+  <data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
+    <value>595, 274</value>
+  </data>
+  <data name="$this.StartPosition" type="System.Windows.Forms.FormStartPosition, System.Windows.Forms">
+    <value>CenterParent</value>
+  </data>
+  <data name="$this.Text" xml:space="preserve">
+    <value>Validation Results</value>
+  </data>
+  <data name="&gt;&gt;columnHeader1.Name" xml:space="preserve">
+    <value>columnHeader1</value>
+  </data>
+  <data name="&gt;&gt;columnHeader1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;columnHeader2.Name" xml:space="preserve">
+    <value>columnHeader2</value>
+  </data>
+  <data name="&gt;&gt;columnHeader2.Type" xml:space="preserve">
+    <value>System.Windows.Forms.ColumnHeader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;imageList1.Name" xml:space="preserve">
+    <value>imageList1</value>
+  </data>
+  <data name="&gt;&gt;imageList1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.ImageList, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;saveFileDialog1.Name" xml:space="preserve">
+    <value>saveFileDialog1</value>
+  </data>
+  <data name="&gt;&gt;saveFileDialog1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.SaveFileDialog, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;$this.Name" xml:space="preserve">
+    <value>ValidationResults</value>
+  </data>
+  <data name="&gt;&gt;$this.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+</root>
\ No newline at end of file

Modified: sandbox/maestro-3.0/Maestro.Editors/Maestro.Editors.csproj
===================================================================
--- sandbox/maestro-3.0/Maestro.Editors/Maestro.Editors.csproj	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Editors/Maestro.Editors.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{5AD2CDBA-952E-4148-98A1-31D2E0D540D5}</ProjectGuid>
     <OutputType>Library</OutputType>


Property changes on: sandbox/maestro-3.0/Maestro.ResourceValidation
___________________________________________________________________
Added: svn:ignore
   + obj
bin


Added: sandbox/maestro-3.0/Maestro.ResourceValidation/ApplicationDefinitionValidator.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/ApplicationDefinitionValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/ApplicationDefinitionValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,92 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+using OSGeo.MapGuide.ObjectModels.ApplicationDefinition;
+using OSGeo.MapGuide.ObjectModels.MapDefinition;
+using OSGeo.MapGuide.ObjectModels.Common;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+using OSGeo.MapGuide.MaestroAPI.ObjectModels;
+
+namespace Maestro.ResourceValidation
+{
+    public class ApplicationDefinitionValidator : IResourceValidator
+    {
+        public ValidationIssue[] Validate(IResource resource, bool recurse)
+        {
+            if (resource.ResourceType != OSGeo.MapGuide.MaestroAPI.ResourceTypes.ApplicationDefinition)
+                return null;
+
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+
+            ApplicationDefinitionType fusionApp = resource as ApplicationDefinitionType;
+            if (fusionApp.MapSet == null || fusionApp.MapSet.Count == 0)
+                issues.Add(new ValidationIssue(fusionApp, ValidationStatus.Error, string.Format(Properties.Resources.ADF_MapMissingError)));
+            else
+            {
+                if (recurse)
+                {
+                    foreach (MapGroupType mapGroup in fusionApp.MapSet)
+                        foreach (OSGeo.MapGuide.ObjectModels.ApplicationDefinition.MapType map in mapGroup.Map)
+                        {
+                            try
+                            {
+
+                                if (map.Extension == null || map.Extension.GetElement("ResourceId") == null)
+                                {
+                                    issues.Add(new ValidationIssue(fusionApp, ValidationStatus.Error, string.Format(Properties.Resources.ADF_MapInvalidError, mapGroup.id)));
+                                }
+                                else
+                                {
+                                    MapDefinition mdef = (MapDefinition)fusionApp.CurrentConnection.ResourceService.GetResource(map.Extension.GetElement("ResourceId").InnerText);
+
+                                    issues.AddRange(ResourceValidatorSet.Validate(mdef, true));
+
+                                    Envelope mapEnv = ObjectFactory.CreateEnvelope(mdef.Extents.MinX, mdef.Extents.MaxX, mdef.Extents.MinY, mdef.Extents.MaxY);
+
+                                    if (mapGroup.InitialView != null)
+                                    {
+                                        if (!mapEnv.Contains(mapGroup.InitialView.CenterX, mapGroup.InitialView.CenterY))
+                                            issues.Add(new ValidationIssue(mdef, ValidationStatus.Warning, string.Format(Properties.Resources.ADF_ViewOutsideMapExtents)));
+                                    }
+                                }
+
+
+                            }
+                            catch (Exception ex)
+                            {
+                                string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                                issues.Add(new ValidationIssue(fusionApp, ValidationStatus.Error, string.Format(Properties.Resources.ADF_MapValidationError, mapGroup.id, msg)));
+                            }
+                        }
+                }
+            }
+
+            return issues.ToArray();
+        }
+
+        public ResourceTypeDescriptor SupportedResourceAndVersion
+        {
+            get { return new ResourceTypeDescriptor(OSGeo.MapGuide.MaestroAPI.ResourceTypes.ApplicationDefinition, "1.0.0"); }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/FeatureSourceValidator.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/FeatureSourceValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/FeatureSourceValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,109 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.ObjectModels.FeatureSource;
+using OSGeo.MapGuide.ObjectModels.Common;
+
+namespace Maestro.ResourceValidation
+{
+    public class FeatureSourceValidator : IResourceValidator
+    {
+        public ValidationIssue[] Validate(IResource resource, bool recurse)
+        {
+            if (resource.ResourceType != ResourceTypes.FeatureSource)
+                return null;
+
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+
+            FeatureSourceType feature = (FeatureSourceType)resource;
+            //Note: Must be saved!
+            string s = feature.CurrentConnection.FeatureService.TestConnection(feature.ResourceID);
+            if (s.Trim().ToUpper() != true.ToString().ToUpper())
+                return new ValidationIssue[] { new ValidationIssue(feature, ValidationStatus.Error, Properties.Resources.FS_ConnectionTestFailed) };
+
+            try
+            {
+                System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.InvariantCulture;
+                FdoSpatialContextList lst = feature.GetSpatialInfo();
+                if (lst == null || lst.SpatialContext == null || lst.SpatialContext.Count == 0)
+                    issues.Add(new ValidationIssue(feature, ValidationStatus.Warning, Properties.Resources.FS_NoSpatialContextWarning));
+                else
+                    foreach (FdoSpatialContextListSpatialContext c in lst.SpatialContext)
+                        if (c.Extent == null || c.Extent.LowerLeftCoordinate == null || c.Extent.UpperRightCoordinate == null)
+                            issues.Add(new ValidationIssue(feature, ValidationStatus.Warning, Properties.Resources.FS_EmptySpatialContextWarning));
+                        else if (double.Parse(c.Extent.LowerLeftCoordinate.X, ci) <= -1000000 && double.Parse(c.Extent.LowerLeftCoordinate.Y, ci) <= -1000000 && double.Parse(c.Extent.UpperRightCoordinate.X, ci) >= 1000000 && double.Parse(c.Extent.UpperRightCoordinate.Y, ci) >= 1000000)
+                            issues.Add(new ValidationIssue(feature, ValidationStatus.Warning, Properties.Resources.FS_DefaultSpatialContextWarning));
+            }
+            catch (Exception ex)
+            {
+                string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                issues.Add(new ValidationIssue(feature, ValidationStatus.Error, string.Format(Properties.Resources.FS_SpatialContextReadError, msg)));
+            }
+
+            List<string> classes = new List<string>();
+            try
+            {
+                FeatureSourceDescription fsd = feature.Describe();
+                if (fsd == null || fsd.Schemas == null || fsd.Schemas.Length == 0)
+                    issues.Add(new ValidationIssue(feature, ValidationStatus.Warning, Properties.Resources.FS_SchemasMissingWarning));
+                else
+                    foreach (FeatureSourceDescription.FeatureSourceSchema scm in fsd.Schemas)
+                        classes.Add(scm.FullnameDecoded);
+            }
+            catch (Exception ex)
+            {
+                string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                issues.Add(new ValidationIssue(feature, ValidationStatus.Error, string.Format(Properties.Resources.FS_SchemaReadError, msg)));
+            }
+
+
+            foreach (string cl in classes)
+            {
+                try
+                {
+                    string[] ids = feature.GetIdentityProperties(cl);
+                    //According to my tests, this code path never gets reached because the 
+                    //MG server will incorrectly throw MgClassNotFoundException when querying
+                    //a class with no identity properties. Nevertheless we'll leave this in, if/when
+                    //this logic is fixed server-side.
+                    if (ids == null || ids.Length == 0)
+                        issues.Add(new ValidationIssue(feature, ValidationStatus.Information, string.Format(Properties.Resources.FS_PrimaryKeyMissingInformation, cl)));
+                }
+                catch (Exception ex)
+                {
+                    string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                    issues.Add(new ValidationIssue(feature, ValidationStatus.Error, string.Format(Properties.Resources.FS_PrimaryKeyReadError, msg)));
+                }
+            }
+
+            return issues.ToArray();
+        }
+
+        public ResourceTypeDescriptor SupportedResourceAndVersion
+        {
+            get { return new ResourceTypeDescriptor(ResourceTypes.FeatureSource, "1.0.0"); }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/LayerDefinitionValidator.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/LayerDefinitionValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/LayerDefinitionValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,175 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+using OSGeo.MapGuide.ObjectModels.LayerDefinition;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.ObjectModels.FeatureSource;
+
+namespace Maestro.ResourceValidation
+{
+    public class LayerDefinitionValidator : IResourceValidator
+    {
+        public ValidationIssue[] Validate(IResource resource, bool recurse)
+        {
+            if (resource.ResourceType != OSGeo.MapGuide.MaestroAPI.ResourceTypes.LayerDefinition)
+                return null;
+
+            if (resource.ResourceVersion != new Version(1, 0, 0))
+                return null;
+
+            LayerDefinition ldef = resource as LayerDefinition;
+            VectorLayerDefinitionType vldef = ldef.Item as VectorLayerDefinitionType;
+            GridLayerDefinitionType gldef = ldef.Item as GridLayerDefinitionType;
+            DrawingLayerDefinitionType dldef = ldef.Item as DrawingLayerDefinitionType;
+
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+
+            if (ldef.Item == null)
+                issues.Add(new ValidationIssue(resource, ValidationStatus.Error, Properties.Resources.LDF_LayerNullError));
+            else if (vldef == null && gldef == null)
+                issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, Properties.Resources.LDF_DrawingLayerDefinitionNotSupportedWarning));
+
+            if (vldef != null)
+            {
+                if (string.IsNullOrEmpty(vldef.FeatureName))
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, Properties.Resources.LDF_MissingFeatureSourceError));
+                if (string.IsNullOrEmpty(vldef.Geometry))
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, Properties.Resources.LDF_MissingGeometryError));
+
+                if (vldef.VectorScaleRange == null || vldef.VectorScaleRange.Count == 0)
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, Properties.Resources.LDF_MissingScaleRangesError));
+                else
+                {
+                    //Test for overlapping scale ranges
+                    List<KeyValuePair<double, double>> ranges = new List<KeyValuePair<double, double>>();
+                    foreach (VectorScaleRangeType vsr in vldef.VectorScaleRange)
+                        ranges.Add(new KeyValuePair<double, double>(
+                            vsr.MaxScaleSpecified ? vsr.MaxScale : double.PositiveInfinity,
+                            vsr.MinScaleSpecified ? vsr.MinScale : double.NegativeInfinity));
+
+                    double min = double.PositiveInfinity;
+                    double max = double.NegativeInfinity;
+                    foreach (KeyValuePair<double, double> sr in ranges)
+                    {
+                        min = Math.Min(min, sr.Value);
+                        max = Math.Max(max, sr.Key);
+                        if (sr.Key < sr.Value)
+                            issues.Add(new ValidationIssue(resource, ValidationStatus.Error, string.Format(Properties.Resources.LDF_MinAndMaxScaleSwappedError, sr.Value, sr.Key)));
+                    }
+
+                    //TODO: Detect gaps in scale ranges
+                    for (int i = 0; i < ranges.Count; i++)
+                        for (int j = i + 1; j < ranges.Count; j++)
+                            if (ranges[i].Key > ranges[j].Value || ranges[i].Value > ranges[j].Value)
+                                issues.Add(new ValidationIssue(resource, ValidationStatus.Information, string.Format(Properties.Resources.LDF_ScaleRangesOverlapInformation, ranges[i].Value, ranges[i].Key, ranges[j].Value, ranges[j].Key)));
+
+                }
+            }
+            else if (gldef != null)
+            {
+                if (gldef.GridScaleRange == null || gldef.GridScaleRange.Count == 0)
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, Properties.Resources.LDF_MissingScaleRangesError));
+                else if (gldef.GridScaleRange.Count != 1)
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, Properties.Resources.LDF_MultipleScaleRangesWarning));
+            }
+            else if (dldef != null)
+                issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, Properties.Resources.LDF_DrawingLayerNotSupportedWarning));
+            else
+                issues.Add(new ValidationIssue(resource, ValidationStatus.Warning, Properties.Resources.LDF_UnsupportedLayerTypeWarning));
+
+            if (recurse)
+            {
+                try
+                {
+                    FeatureSourceType fs = (FeatureSourceType)ldef.CurrentConnection.ResourceService.GetResource(ldef.Item.ResourceId);
+                    issues.AddRange(ResourceValidatorSet.Validate(fs, recurse));
+
+                    try
+                    {
+                        if (vldef != null || gldef != null)
+                        {
+                            string schema = vldef == null ? gldef.FeatureName : vldef.FeatureName;
+                            string geometry = vldef == null ? gldef.Geometry : vldef.Geometry;
+
+                            bool foundSchema = false;
+                            bool foundGeometry = false;
+
+                            FeatureSourceDescription desc = fs.Describe();
+                            foreach (FeatureSourceDescription.FeatureSourceSchema scm in desc.Schemas)
+                                if (scm.FullnameDecoded == schema)
+                                {
+                                    foundSchema = true;
+
+                                    foreach (FeatureSetColumn col in scm.Columns)
+                                        if (col.Name == geometry)
+                                        {
+                                            foundGeometry = true;
+                                            break;
+                                        }
+
+
+                                    if (vldef != null && vldef.PropertyMapping != null)
+                                        foreach (NameStringPairType s in vldef.PropertyMapping)
+                                        {
+                                            bool found = false;
+                                            foreach (FeatureSetColumn col in scm.Columns)
+                                                if (col.Name == s.Name)
+                                                {
+                                                    found = true;
+                                                    break;
+                                                }
+
+                                            if (!found)
+                                                issues.Add(new ValidationIssue(resource, ValidationStatus.Error, string.Format(Properties.Resources.LDF_SchemaMissingError, schema, fs.ResourceID)));
+
+                                        }
+
+                                    break;
+                                }
+
+                            if (!foundSchema)
+                                issues.Add(new ValidationIssue(resource, ValidationStatus.Error, string.Format(Properties.Resources.LDF_SchemaMissingError, schema, fs.ResourceID)));
+                            else if (!foundGeometry)
+                                issues.Add(new ValidationIssue(resource, ValidationStatus.Error, string.Format(Properties.Resources.LDF_GeometryMissingError, geometry, schema, fs.ResourceID)));
+                        }
+                    }
+                    catch (Exception)
+                    {
+                        issues.Add(new ValidationIssue(fs, ValidationStatus.Error, string.Format(Properties.Resources.LDF_SchemaAndColumnReadFailedError)));
+                    }
+                }
+                catch (Exception)
+                {
+                    issues.Add(new ValidationIssue(resource, ValidationStatus.Error, string.Format(Properties.Resources.LDF_FeatureSourceLoadError)));
+                }
+            }
+
+            return issues.ToArray();
+        }
+
+        public ResourceTypeDescriptor SupportedResourceAndVersion
+        {
+            get { return new ResourceTypeDescriptor(ResourceTypes.LayerDefinition, "1.0.0"); }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/Maestro.ResourceValidation.csproj
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/Maestro.ResourceValidation.csproj	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/Maestro.ResourceValidation.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{3B29C138-666C-46D0-A55D-824E5192C091}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Maestro.ResourceValidation</RootNamespace>
+    <AssemblyName>Maestro.ResourceValidation</AssemblyName>
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ApplicationDefinitionValidator.cs" />
+    <Compile Include="FeatureSourceValidator.cs" />
+    <Compile Include="LayerDefinitionValidator.cs" />
+    <Compile Include="MapDefinitionValidator.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Resources.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTime>True</DesignTime>
+      <DependentUpon>Resources.resx</DependentUpon>
+    </Compile>
+    <Compile Include="ResourceValidatorLoader.cs" />
+    <Compile Include="WebLayoutValidator.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\OSGeo.MapGuide.MaestroAPI\OSGeo.MapGuide.MaestroAPI.csproj">
+      <Project>{80FA3158-8B5F-48D1-A393-0378AFE48A7E}</Project>
+      <Name>OSGeo.MapGuide.MaestroAPI</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <EmbeddedResource Include="Properties\Resources.resx">
+      <Generator>ResXFileCodeGenerator</Generator>
+      <LastGenOutput>Resources.Designer.cs</LastGenOutput>
+    </EmbeddedResource>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/MapDefinitionValidator.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/MapDefinitionValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/MapDefinitionValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,164 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+using OSGeo.MapGuide.ObjectModels.MapDefinition;
+using OSGeo.MapGuide.ObjectModels.LayerDefinition;
+using OSGeo.MapGuide.ObjectModels.FeatureSource;
+using OSGeo.MapGuide.ObjectModels.Common;
+using OSGeo.MapGuide.MaestroAPI.ObjectModels;
+using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+
+namespace Maestro.ResourceValidation
+{
+    public class MapDefinitionValidator : IResourceValidator
+    {
+        public ValidationIssue[] Validate(IResource resource, bool recurse)
+        {
+            if (resource.ResourceType != ResourceTypes.MapDefinition)
+                return null;
+
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+
+            MapDefinition mdef = resource as MapDefinition;
+
+            foreach (MapLayerGroupType g in mdef.MapLayerGroup)
+                if (g.ShowInLegend && (g.LegendLabel == null || g.LegendLabel.Trim().Length == 0))
+                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, string.Format(Properties.Resources.MDF_GroupMissingLabelInformation, g.Name)));
+                else if (g.ShowInLegend && g.LegendLabel.Trim().ToLower() == "layer group")
+                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, string.Format(Properties.Resources.MDF_GroupHasDefaultLabelInformation, g.Name)));
+
+            List<BaseMapLayerType> layers = new List<BaseMapLayerType>();
+            foreach (BaseMapLayerType l in mdef.MapLayer)
+                layers.Add(l);
+
+            if (mdef.BaseMapDefinition != null && mdef.BaseMapDefinition.BaseMapLayerGroup != null)
+                foreach (BaseMapLayerGroupCommonType g in mdef.BaseMapDefinition.BaseMapLayerGroup)
+                    foreach (BaseMapLayerType l in g.BaseMapLayer)
+                        layers.Add(l);
+
+            Dictionary<string, BaseMapLayerType> nameCounter = new Dictionary<string, BaseMapLayerType>();
+
+            foreach (BaseMapLayerType l in layers)
+            {
+                if (nameCounter.ContainsKey(l.Name))
+                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Warning, string.Format(Properties.Resources.MDF_LayerNameDuplicateWarning, l.Name, l.ResourceId, nameCounter[l.Name].ResourceId)));
+                else
+                    nameCounter.Add(l.Name, l);
+
+                if (l.ShowInLegend && (string.IsNullOrEmpty(l.LegendLabel) || l.LegendLabel.Trim().Length == 0))
+                    issues.Add(new ValidationIssue(mdef, ValidationStatus.Information, string.Format(Properties.Resources.MDF_LayerMissingLabelInformation, l.Name)));
+
+                if (recurse)
+                {
+                    var mapEnv = ObjectFactory.CreateEnvelope(mdef.Extents.MinX, mdef.Extents.MaxX, mdef.Extents.MinY, mdef.Extents.MaxY);
+
+                    try
+                    {
+                        ILayerDefinition layer = null;
+                        IResource res = mdef.CurrentConnection.ResourceService.GetResource(l.ResourceId);
+                        if (!ResourceValidatorSet.HasValidator(res.ResourceType, res.ResourceVersion))
+                        {
+                            //Need to trap the no registered validator message
+                            issues.AddRange(ResourceValidatorSet.Validate(res, true));
+                            continue;
+                        }
+
+                        layer = (ILayerDefinition)res;
+                        issues.AddRange(ResourceValidatorSet.Validate(layer, true));
+
+                        IVectorLayerDefinition vl = null;
+                        if (layer.SubLayer.LayerType == LayerType.Vector)
+                            vl = (IVectorLayerDefinition)layer.SubLayer;
+
+                        if (vl != null)
+                        {
+                            try
+                            {
+                                FeatureSourceType fs = (FeatureSourceType)layer.CurrentConnection.ResourceService.GetResource(vl.FeatureSourceID);
+                                //The layer recurses on the FeatureSource
+                                //issues.AddRange(Validation.Validate(fs, true));
+
+                                try
+                                {
+                                    FdoSpatialContextList context = fs.GetSpatialInfo();
+                                    if (context.SpatialContext == null || context.SpatialContext.Count == 0)
+                                        issues.Add(new ValidationIssue(fs, ValidationStatus.Warning, string.Format(Properties.Resources.MDF_MissingSpatialContextWarning, fs.ResourceID)));
+                                    else
+                                    {
+                                        if (context.SpatialContext.Count > 1)
+                                            issues.Add(new ValidationIssue(fs, ValidationStatus.Information, string.Format(Properties.Resources.MDF_MultipleSpatialContextsInformation, fs.ResourceID)));
+
+
+                                        bool skipGeomCheck = false;
+
+                                        //TODO: Switch to the correct version (2.1), once released
+                                        if (context.SpatialContext[0].CoordinateSystemWkt != mdef.CoordinateSystem)
+                                        {
+                                            if (layer.SubLayer.LayerType == LayerType.Raster && mdef.CurrentConnection.SiteVersion <= SiteVersions.GetVersion(OSGeo.MapGuide.MaestroAPI.KnownSiteVersions.MapGuideOS2_0_2))
+                                                issues.Add(new ValidationIssue(fs, ValidationStatus.Error, string.Format(Properties.Resources.MDF_RasterReprojectionError, fs.ResourceID)));
+                                            else
+                                                issues.Add(new ValidationIssue(fs, ValidationStatus.Warning, string.Format(Properties.Resources.MDF_DataReprojectionWarning, fs.ResourceID)));
+
+                                            skipGeomCheck = true;
+                                        }
+
+                                        if (vl.GeometryProperty != null && !skipGeomCheck)
+                                        {
+                                            var env = fs.GetSpatialExtent(vl.FullQualifiedClassName, vl.GeometryProperty);
+                                            if (!env.Intersects(mapEnv))
+                                                issues.Add(new ValidationIssue(fs, ValidationStatus.Warning, string.Format(Properties.Resources.MDF_DataOutsideMapWarning, fs.ResourceID)));
+                                        }
+                                    }
+                                }
+                                catch (Exception ex)
+                                {
+                                    string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                                    issues.Add(new ValidationIssue(layer, ValidationStatus.Error, string.Format(Properties.Resources.MDF_ResourceReadError, fs.ResourceID, msg)));
+                                }
+                            }
+                            catch (Exception ex)
+                            {
+                                string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                                issues.Add(new ValidationIssue(mdef, ValidationStatus.Error, string.Format(Properties.Resources.MDF_FeatureSourceReadError, l.ResourceId, msg)));
+                            }
+                        }
+
+                    }
+                    catch (Exception ex)
+                    {
+                        string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                        issues.Add(new ValidationIssue(mdef, ValidationStatus.Error, string.Format(Properties.Resources.MDF_LayerReadError, l.ResourceId, msg)));
+                    }
+                }
+            }
+
+            return issues.ToArray();
+        }
+
+        public ResourceTypeDescriptor SupportedResourceAndVersion
+        {
+            get { return new ResourceTypeDescriptor(ResourceTypes.MapDefinition, "1.0.0"); }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/AssemblyInfo.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/AssemblyInfo.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/AssemblyInfo.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Maestro.ResourceValidation")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Maestro.ResourceValidation")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2010")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("ffa594f1-d7e3-403c-9eb4-3565007f4209")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.Designer.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.Designer.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.Designer.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,477 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     This code was generated by a tool.
+//     Runtime Version:2.0.50727.4927
+//
+//     Changes to this file may cause incorrect behavior and will be lost if
+//     the code is regenerated.
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace Maestro.ResourceValidation.Properties {
+    using System;
+    
+    
+    /// <summary>
+    ///   A strongly-typed resource class, for looking up localized strings, etc.
+    /// </summary>
+    // This class was auto-generated by the StronglyTypedResourceBuilder
+    // class via a tool like ResGen or Visual Studio.
+    // To add or remove a member, edit your .ResX file then rerun ResGen
+    // with the /str option, or rebuild your VS project.
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    internal class Resources {
+        
+        private static global::System.Resources.ResourceManager resourceMan;
+        
+        private static global::System.Globalization.CultureInfo resourceCulture;
+        
+        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+        internal Resources() {
+        }
+        
+        /// <summary>
+        ///   Returns the cached ResourceManager instance used by this class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Resources.ResourceManager ResourceManager {
+            get {
+                if (object.ReferenceEquals(resourceMan, null)) {
+                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Maestro.ResourceValidation.Properties.Resources", typeof(Resources).Assembly);
+                    resourceMan = temp;
+                }
+                return resourceMan;
+            }
+        }
+        
+        /// <summary>
+        ///   Overrides the current thread's CurrentUICulture property for all
+        ///   resource lookups using this strongly typed resource class.
+        /// </summary>
+        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+        internal static global::System.Globalization.CultureInfo Culture {
+            get {
+                return resourceCulture;
+            }
+            set {
+                resourceCulture = value;
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Map with ID {0} does not point to a valid map.
+        /// </summary>
+        internal static string ADF_MapInvalidError {
+            get {
+                return ResourceManager.GetString("ADF_MapInvalidError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Fusion application does not specify a map.
+        /// </summary>
+        internal static string ADF_MapMissingError {
+            get {
+                return ResourceManager.GetString("ADF_MapMissingError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Error validating MapDefinition {0}, message: {1}.
+        /// </summary>
+        internal static string ADF_MapValidationError {
+            get {
+                return ResourceManager.GetString("ADF_MapValidationError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Fusion application specifies a start view that is outside the map&apos;s initial extents.
+        /// </summary>
+        internal static string ADF_ViewOutsideMapExtents {
+            get {
+                return ResourceManager.GetString("ADF_ViewOutsideMapExtents", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Connection test failed.
+        /// </summary>
+        internal static string FS_ConnectionTestFailed {
+            get {
+                return ResourceManager.GetString("FS_ConnectionTestFailed", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Spatial context extent appears to be invalid (or default).
+        /// </summary>
+        internal static string FS_DefaultSpatialContextWarning {
+            get {
+                return ResourceManager.GetString("FS_DefaultSpatialContextWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Empty spatial context extent detected.
+        /// </summary>
+        internal static string FS_EmptySpatialContextWarning {
+            get {
+                return ResourceManager.GetString("FS_EmptySpatialContextWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Could not obtain identity properties for {0}. This class either has no identity properties and/or is derived from a view with no uniquely identifiable columns. Attempting to create feature joins from this class may produce unexpected results..
+        /// </summary>
+        internal static string FS_NoPrimaryKeyOrView {
+            get {
+                return ResourceManager.GetString("FS_NoPrimaryKeyOrView", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No spatial contexts found. If this feature source is strictly used for Feature Joins, this warning can be safely ignored.
+        /// </summary>
+        internal static string FS_NoSpatialContextWarning {
+            get {
+                return ResourceManager.GetString("FS_NoSpatialContextWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No primary key defined for class: {0}, features will not be selectable and feature joins on this class may produce unexpected results.
+        /// </summary>
+        internal static string FS_PrimaryKeyMissingInformation {
+            get {
+                return ResourceManager.GetString("FS_PrimaryKeyMissingInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to read identity properties: {0}.
+        /// </summary>
+        internal static string FS_PrimaryKeyReadError {
+            get {
+                return ResourceManager.GetString("FS_PrimaryKeyReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to read schema description: {0}.
+        /// </summary>
+        internal static string FS_SchemaReadError {
+            get {
+                return ResourceManager.GetString("FS_SchemaReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No schemas found in datasource.
+        /// </summary>
+        internal static string FS_SchemasMissingWarning {
+            get {
+                return ResourceManager.GetString("FS_SchemasMissingWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to read spatial context: {0}.
+        /// </summary>
+        internal static string FS_SpatialContextReadError {
+            get {
+                return ResourceManager.GetString("FS_SpatialContextReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Only vector layers and raster layers are currently validated.
+        /// </summary>
+        internal static string LDF_DrawingLayerDefinitionNotSupportedWarning {
+            get {
+                return ResourceManager.GetString("LDF_DrawingLayerDefinitionNotSupportedWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Maestro does not support DrawingLayers.
+        /// </summary>
+        internal static string LDF_DrawingLayerNotSupportedWarning {
+            get {
+                return ResourceManager.GetString("LDF_DrawingLayerNotSupportedWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to load featuresource.
+        /// </summary>
+        internal static string LDF_FeatureSourceLoadError {
+            get {
+                return ResourceManager.GetString("LDF_FeatureSourceLoadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to find geometry column {0} in schema {1} on featuresource {2}.
+        /// </summary>
+        internal static string LDF_GeometryMissingError {
+            get {
+                return ResourceManager.GetString("LDF_GeometryMissingError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer is missing core information.
+        /// </summary>
+        internal static string LDF_LayerNullError {
+            get {
+                return ResourceManager.GetString("LDF_LayerNullError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The minimum scale ({0}) is larger than the maximum scale ({1}).
+        /// </summary>
+        internal static string LDF_MinAndMaxScaleSwappedError {
+            get {
+                return ResourceManager.GetString("LDF_MinAndMaxScaleSwappedError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No FeatureSource is assigned to the layer.
+        /// </summary>
+        internal static string LDF_MissingFeatureSourceError {
+            get {
+                return ResourceManager.GetString("LDF_MissingFeatureSourceError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No Geometry is assigned to the layer.
+        /// </summary>
+        internal static string LDF_MissingGeometryError {
+            get {
+                return ResourceManager.GetString("LDF_MissingGeometryError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to No scale ranges are defined, no data can be displayed.
+        /// </summary>
+        internal static string LDF_MissingScaleRangesError {
+            get {
+                return ResourceManager.GetString("LDF_MissingScaleRangesError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to More than one scale ranges is defined, this is valid, but unsupported by Maestro.
+        /// </summary>
+        internal static string LDF_MultipleScaleRangesWarning {
+            get {
+                return ResourceManager.GetString("LDF_MultipleScaleRangesWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The scale range {0}-{1} overlaps the range {2}-{3}.
+        /// </summary>
+        internal static string LDF_ScaleRangesOverlapInformation {
+            get {
+                return ResourceManager.GetString("LDF_ScaleRangesOverlapInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to validate column and schema.
+        /// </summary>
+        internal static string LDF_SchemaAndColumnReadFailedError {
+            get {
+                return ResourceManager.GetString("LDF_SchemaAndColumnReadFailedError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Failed to find schema {0} in featuresource {1}.
+        /// </summary>
+        internal static string LDF_SchemaMissingError {
+            get {
+                return ResourceManager.GetString("LDF_SchemaMissingError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The layer has no type, or the type is unsupported by Maestro.
+        /// </summary>
+        internal static string LDF_UnsupportedLayerTypeWarning {
+            get {
+                return ResourceManager.GetString("LDF_UnsupportedLayerTypeWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Data from {0} is not visible in the map&apos;s start extent.
+        /// </summary>
+        internal static string MDF_DataOutsideMapWarning {
+            get {
+                return ResourceManager.GetString("MDF_DataOutsideMapWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} has a different coordinate system than the map, this will impact performance as the coordinates are transformed while rendering the map. Maestro cannot validate the extent of the data..
+        /// </summary>
+        internal static string MDF_DataReprojectionWarning {
+            get {
+                return ResourceManager.GetString("MDF_DataReprojectionWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0}&apos;s FeatureSource could not be read: {1}.
+        /// </summary>
+        internal static string MDF_FeatureSourceReadError {
+            get {
+                return ResourceManager.GetString("MDF_FeatureSourceReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Group {0} has the default legend label.
+        /// </summary>
+        internal static string MDF_GroupHasDefaultLabelInformation {
+            get {
+                return ResourceManager.GetString("MDF_GroupHasDefaultLabelInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Group {0} does not have a legend label.
+        /// </summary>
+        internal static string MDF_GroupMissingLabelInformation {
+            get {
+                return ResourceManager.GetString("MDF_GroupMissingLabelInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0} does not have a legend label.
+        /// </summary>
+        internal static string MDF_LayerMissingLabelInformation {
+            get {
+                return ResourceManager.GetString("MDF_LayerMissingLabelInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to The layer name {0} is used for both &quot;{1}&quot; and &quot;{2}&quot;.
+        /// </summary>
+        internal static string MDF_LayerNameDuplicateWarning {
+            get {
+                return ResourceManager.GetString("MDF_LayerNameDuplicateWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0} could not be read: {1}.
+        /// </summary>
+        internal static string MDF_LayerReadError {
+            get {
+                return ResourceManager.GetString("MDF_LayerReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0} has no geometry column.
+        /// </summary>
+        internal static string MDF_MissingLayerGeometryColumnError {
+            get {
+                return ResourceManager.GetString("MDF_MissingLayerGeometryColumnError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0} has an invalid Schema.
+        /// </summary>
+        internal static string MDF_MissingLayerSchemaError {
+            get {
+                return ResourceManager.GetString("MDF_MissingLayerSchemaError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} has no spatial context (eg. no coordinate system).
+        /// </summary>
+        internal static string MDF_MissingSpatialContextWarning {
+            get {
+                return ResourceManager.GetString("MDF_MissingSpatialContextWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} has more than one spatial context, the following test results may be inacurate.
+        /// </summary>
+        internal static string MDF_MultipleSpatialContextsInformation {
+            get {
+                return ResourceManager.GetString("MDF_MultipleSpatialContextsInformation", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} is a raster layer, and in another coordinate system than the map. No data will be displayed by the layer.
+        /// </summary>
+        internal static string MDF_RasterReprojectionError {
+            get {
+                return ResourceManager.GetString("MDF_RasterReprojectionError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to {0} could not be processed for spatial info: {1}.
+        /// </summary>
+        internal static string MDF_ResourceReadError {
+            get {
+                return ResourceManager.GetString("MDF_ResourceReadError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layer {0} is a type that is unsupported by Maestro.
+        /// </summary>
+        internal static string MDF_UnsupportedLayerTypeWarning {
+            get {
+                return ResourceManager.GetString("MDF_UnsupportedLayerTypeWarning", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Error validating MapDefinition {0}, message: {1}.
+        /// </summary>
+        internal static string WL_MapValidationError {
+            get {
+                return ResourceManager.GetString("WL_MapValidationError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layout does not specify a map.
+        /// </summary>
+        internal static string WL_MissingMapError {
+            get {
+                return ResourceManager.GetString("WL_MissingMapError", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Layout specifies a start view that is outside the map&apos;s initial extents.
+        /// </summary>
+        internal static string WL_StartViewOutsideExtentsWarning {
+            get {
+                return ResourceManager.GetString("WL_StartViewOutsideExtentsWarning", resourceCulture);
+            }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.resx
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.resx	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/Properties/Resources.resx	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <data name="ADF_MapInvalidError" xml:space="preserve">
+    <value>Map with ID {0} does not point to a valid map</value>
+    <comment>An error message that is displayed if there is a MapID that does not reference a valid map</comment>
+  </data>
+  <data name="ADF_MapMissingError" xml:space="preserve">
+    <value>Fusion application does not specify a map</value>
+    <comment>An error message that is displayed if the map is missing</comment>
+  </data>
+  <data name="ADF_MapValidationError" xml:space="preserve">
+    <value>Error validating MapDefinition {0}, message: {1}</value>
+    <comment>An error message that is displayed if an error occurs while validating a map</comment>
+  </data>
+  <data name="ADF_ViewOutsideMapExtents" xml:space="preserve">
+    <value>Fusion application specifies a start view that is outside the map's initial extents</value>
+    <comment>An error message that is displayed if the start view is outside the defined map</comment>
+  </data>
+  <data name="FS_ConnectionTestFailed" xml:space="preserve">
+    <value>Connection test failed</value>
+    <comment>An error message that is displayed if a connection test on the feature source failed</comment>
+  </data>
+  <data name="FS_DefaultSpatialContextWarning" xml:space="preserve">
+    <value>Spatial context extent appears to be invalid (or default)</value>
+    <comment>A warning message that is displayed if the spatial context has the default extent</comment>
+  </data>
+  <data name="FS_EmptySpatialContextWarning" xml:space="preserve">
+    <value>Empty spatial context extent detected</value>
+    <comment>A warning message that is displayed when the spatial context is empty</comment>
+  </data>
+  <data name="FS_NoPrimaryKeyOrView" xml:space="preserve">
+    <value>Could not obtain identity properties for {0}. This class either has no identity properties and/or is derived from a view with no uniquely identifiable columns. Attempting to create feature joins from this class may produce unexpected results.</value>
+    <comment>A warning message that is displayed when we can't obtain identity properties from a class because it is a view</comment>
+  </data>
+  <data name="FS_NoSpatialContextWarning" xml:space="preserve">
+    <value>No spatial contexts found. If this feature source is strictly used for Feature Joins, this warning can be safely ignored</value>
+    <comment>A warning message that is displayed when the featuresource fails to report its spatial context</comment>
+  </data>
+  <data name="FS_PrimaryKeyMissingInformation" xml:space="preserve">
+    <value>No primary key defined for class: {0}, features will not be selectable and feature joins on this class may produce unexpected results</value>
+    <comment>An information message that is displayed if the schema has no primary key defined</comment>
+  </data>
+  <data name="FS_PrimaryKeyReadError" xml:space="preserve">
+    <value>Failed to read identity properties: {0}</value>
+    <comment>An error message that is displayed if the primary key information could not be read</comment>
+  </data>
+  <data name="FS_SchemaReadError" xml:space="preserve">
+    <value>Failed to read schema description: {0}</value>
+    <comment>An error message that is displayed if the schema information could not be read</comment>
+  </data>
+  <data name="FS_SchemasMissingWarning" xml:space="preserve">
+    <value>No schemas found in datasource</value>
+    <comment>A warning message that is displayed if the featuresource has no schemas</comment>
+  </data>
+  <data name="FS_SpatialContextReadError" xml:space="preserve">
+    <value>Failed to read spatial context: {0}</value>
+    <comment>An error message that is displayed if the spatial context could not be read</comment>
+  </data>
+  <data name="LDF_DrawingLayerDefinitionNotSupportedWarning" xml:space="preserve">
+    <value>Only vector layers and raster layers are currently validated</value>
+    <comment>A warning message that is displayed if the layer is not Grid or Vector</comment>
+  </data>
+  <data name="LDF_DrawingLayerNotSupportedWarning" xml:space="preserve">
+    <value>Maestro does not support DrawingLayers</value>
+    <comment>A warning message that is displayed if the layer is a DrawingLayer type</comment>
+  </data>
+  <data name="LDF_FeatureSourceLoadError" xml:space="preserve">
+    <value>Failed to load featuresource</value>
+    <comment>An error message that is displayed if the featuresource fails to load</comment>
+  </data>
+  <data name="LDF_GeometryMissingError" xml:space="preserve">
+    <value>Failed to find geometry column {0} in schema {1} on featuresource {2}</value>
+    <comment>An error message that is displayed if the geometry is not present in the selected schema</comment>
+  </data>
+  <data name="LDF_LayerNullError" xml:space="preserve">
+    <value>Layer is missing core information</value>
+    <comment>An error message that is displayed if the layer has a null reference</comment>
+  </data>
+  <data name="LDF_MinAndMaxScaleSwappedError" xml:space="preserve">
+    <value>The minimum scale ({0}) is larger than the maximum scale ({1})</value>
+    <comment>An error message that is displayed if the scale ranges are reversed</comment>
+  </data>
+  <data name="LDF_MissingFeatureSourceError" xml:space="preserve">
+    <value>No FeatureSource is assigned to the layer</value>
+    <comment>An error message that is displayed if the layer has no FeatureSource</comment>
+  </data>
+  <data name="LDF_MissingGeometryError" xml:space="preserve">
+    <value>No Geometry is assigned to the layer</value>
+    <comment>An error message that is displayed if the layer has no geometry column selected</comment>
+  </data>
+  <data name="LDF_MissingScaleRangesError" xml:space="preserve">
+    <value>No scale ranges are defined, no data can be displayed</value>
+    <comment>An error message that is displayed if there are no scale ranges defined</comment>
+  </data>
+  <data name="LDF_MultipleScaleRangesWarning" xml:space="preserve">
+    <value>More than one scale ranges is defined, this is valid, but unsupported by Maestro</value>
+    <comment>A warning message that is displayed if a raster layer has multiple scale ranges defined</comment>
+  </data>
+  <data name="LDF_ScaleRangesOverlapInformation" xml:space="preserve">
+    <value>The scale range {0}-{1} overlaps the range {2}-{3}</value>
+    <comment>A warning message that is displayed if two scaleranges overlap</comment>
+  </data>
+  <data name="LDF_SchemaAndColumnReadFailedError" xml:space="preserve">
+    <value>Failed to validate column and schema</value>
+    <comment>An error message that is displayed if the schema and column read fails</comment>
+  </data>
+  <data name="LDF_SchemaMissingError" xml:space="preserve">
+    <value>Failed to find schema {0} in featuresource {1}</value>
+    <comment>An error message that is displayed if the selected schema is not present in the FeatureSource</comment>
+  </data>
+  <data name="LDF_UnsupportedLayerTypeWarning" xml:space="preserve">
+    <value>The layer has no type, or the type is unsupported by Maestro</value>
+    <comment>A  warning message that is displayed if the layer type is unknown</comment>
+  </data>
+  <data name="MDF_DataOutsideMapWarning" xml:space="preserve">
+    <value>Data from {0} is not visible in the map's start extent</value>
+    <comment>A warning message that is displayed if a layer reports an extent that is outside the map's extent</comment>
+  </data>
+  <data name="MDF_DataReprojectionWarning" xml:space="preserve">
+    <value>{0} has a different coordinate system than the map, this will impact performance as the coordinates are transformed while rendering the map. Maestro cannot validate the extent of the data.</value>
+    <comment>A warning message that is displayed if a layer reports a coordinate system that differs from the map's.</comment>
+  </data>
+  <data name="MDF_FeatureSourceReadError" xml:space="preserve">
+    <value>Layer {0}'s FeatureSource could not be read: {1}</value>
+    <comment>An error message that is displayed if a FeatureSource fails to load</comment>
+  </data>
+  <data name="MDF_GroupHasDefaultLabelInformation" xml:space="preserve">
+    <value>Group {0} has the default legend label</value>
+    <comment>An information message that is displayed if a group is assigned the default group label</comment>
+  </data>
+  <data name="MDF_GroupMissingLabelInformation" xml:space="preserve">
+    <value>Group {0} does not have a legend label</value>
+    <comment>An information message that is displayed if a group has a blank legend label</comment>
+  </data>
+  <data name="MDF_LayerMissingLabelInformation" xml:space="preserve">
+    <value>Layer {0} does not have a legend label</value>
+    <comment>An information message that is displayed if a layer has a blank legend label</comment>
+  </data>
+  <data name="MDF_LayerNameDuplicateWarning" xml:space="preserve">
+    <value>The layer name {0} is used for both "{1}" and "{2}"</value>
+    <comment>A warning message that is displayed if two layers share the same layer name</comment>
+  </data>
+  <data name="MDF_LayerReadError" xml:space="preserve">
+    <value>Layer {0} could not be read: {1}</value>
+    <comment>An error message that is displayed if a LayerDefinition fails to load</comment>
+  </data>
+  <data name="MDF_MissingLayerGeometryColumnError" xml:space="preserve">
+    <value>Layer {0} has no geometry column</value>
+    <comment>An error message that is displayed if a layer has no geometry column specified</comment>
+  </data>
+  <data name="MDF_MissingLayerSchemaError" xml:space="preserve">
+    <value>Layer {0} has an invalid Schema</value>
+    <comment>An error message that is displayed if the layer has no schema specified</comment>
+  </data>
+  <data name="MDF_MissingSpatialContextWarning" xml:space="preserve">
+    <value>{0} has no spatial context (eg. no coordinate system)</value>
+    <comment>A warning message that is displayed if the FeatureSource reports no spatial context</comment>
+  </data>
+  <data name="MDF_MultipleSpatialContextsInformation" xml:space="preserve">
+    <value>{0} has more than one spatial context, the following test results may be inacurate</value>
+    <comment>An information message that is displayed if a FeatureSource reports multiple spatial contexts</comment>
+  </data>
+  <data name="MDF_RasterReprojectionError" xml:space="preserve">
+    <value>{0} is a raster layer, and in another coordinate system than the map. No data will be displayed by the layer</value>
+    <comment>An error message that is displayed if a raster layer reports a different projection than the map, and the MapGuide server version does not support raster reprojection</comment>
+  </data>
+  <data name="MDF_ResourceReadError" xml:space="preserve">
+    <value>{0} could not be processed for spatial info: {1}</value>
+    <comment>An error message that is displayed if a resource fails to load</comment>
+  </data>
+  <data name="MDF_UnsupportedLayerTypeWarning" xml:space="preserve">
+    <value>Layer {0} is a type that is unsupported by Maestro</value>
+    <comment>A warning message that is displayed if a layer has a type that is not supported by Maestro</comment>
+  </data>
+  <data name="WL_MapValidationError" xml:space="preserve">
+    <value>Error validating MapDefinition {0}, message: {1}</value>
+    <comment>An error message that is displayed if the map validation fails</comment>
+  </data>
+  <data name="WL_MissingMapError" xml:space="preserve">
+    <value>Layout does not specify a map</value>
+    <comment>An error message that is displayed if no map is specified in the layout</comment>
+  </data>
+  <data name="WL_StartViewOutsideExtentsWarning" xml:space="preserve">
+    <value>Layout specifies a start view that is outside the map's initial extents</value>
+    <comment>A warning message that is displayed if the layout specifies a start center point that is outside the map's specified extents</comment>
+  </data>
+</root>
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/ResourceValidatorLoader.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/ResourceValidatorLoader.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/ResourceValidatorLoader.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,48 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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 Maestro.ResourceValidation
+{
+    /// <summary>
+    /// Utility class that initializes the default set of resource validators
+    /// </summary>
+    public static class ResourceValidatorLoader
+    {
+        private static bool m_initialized = false;
+
+        public static void LoadStockValidators()
+        {
+            if (m_initialized)
+                return;
+
+            ResourceValidatorSet.RegisterValidator(new FeatureSourceValidator());
+            ResourceValidatorSet.RegisterValidator(new LayerDefinitionValidator());
+            ResourceValidatorSet.RegisterValidator(new MapDefinitionValidator());
+            ResourceValidatorSet.RegisterValidator(new WebLayoutValidator());
+            ResourceValidatorSet.RegisterValidator(new ApplicationDefinitionValidator());
+
+            m_initialized = true;
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.ResourceValidation/WebLayoutValidator.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.ResourceValidation/WebLayoutValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.ResourceValidation/WebLayoutValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,77 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+using OSGeo.MapGuide.ObjectModels.WebLayout;
+using OSGeo.MapGuide.ObjectModels.MapDefinition;
+using OSGeo.MapGuide.MaestroAPI.ObjectModels;
+using OSGeo.MapGuide.MaestroAPI.Exceptions;
+
+namespace Maestro.ResourceValidation
+{
+    public class WebLayoutValidator : IResourceValidator
+    {
+        public ValidationIssue[] Validate(IResource resource, bool recurse)
+        {
+            if (resource.ResourceType != OSGeo.MapGuide.MaestroAPI.ResourceTypes.WebLayout)
+                return null;
+
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+
+            WebLayoutType layout = resource as WebLayoutType;
+            if (layout.Map == null || layout.Map.ResourceId == null)
+                issues.Add(new ValidationIssue(layout, ValidationStatus.Error, string.Format(Properties.Resources.WL_MissingMapError)));
+            else
+            {
+                if (recurse)
+                {
+                    try
+                    {
+                        MapDefinition mdef = (MapDefinition)layout.CurrentConnection.ResourceService.GetResource(layout.Map.ResourceId);
+
+                        issues.AddRange(ResourceValidatorSet.Validate(mdef, true));
+
+                        if (layout.Map.InitialView != null)
+                        {
+                            var mapEnv = ObjectFactory.CreateEnvelope(mdef.Extents.MinX, mdef.Extents.MaxX, mdef.Extents.MinY, mdef.Extents.MaxY);
+                            if (!mapEnv.Contains(layout.Map.InitialView.CenterX, layout.Map.InitialView.CenterY))
+                                issues.Add(new ValidationIssue(mdef, ValidationStatus.Warning, string.Format(Properties.Resources.WL_StartViewOutsideExtentsWarning)));
+                        }
+
+                    }
+                    catch (Exception ex)
+                    {
+                        string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                        issues.Add(new ValidationIssue(layout, ValidationStatus.Error, string.Format(Properties.Resources.WL_MapValidationError, layout.Map.ResourceId, msg)));
+                    }
+                }
+            }
+
+            return issues.ToArray();
+        }
+
+        public ResourceTypeDescriptor SupportedResourceAndVersion
+        {
+            get { return new ResourceTypeDescriptor(OSGeo.MapGuide.MaestroAPI.ResourceTypes.WebLayout, "1.0.0"); }
+        }
+    }
+}

Added: sandbox/maestro-3.0/Maestro.Shared.UI/CancelException.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Shared.UI/CancelException.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Shared.UI/CancelException.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Maestro.Shared.UI
+{
+    [global::System.Serializable]
+    public class CancelException : Exception
+    {
+        //
+        // For guidelines regarding the creation of new exception types, see
+        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconerrorraisinghandlingguidelines.asp
+        // and
+        //    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp07192001.asp
+        //
+
+        public CancelException() { }
+        public CancelException(string message) : base(message) { }
+        public CancelException(string message, Exception inner) : base(message, inner) { }
+        protected CancelException(
+          System.Runtime.Serialization.SerializationInfo info,
+          System.Runtime.Serialization.StreamingContext context)
+            : base(info, context) { }
+    }
+}

Modified: sandbox/maestro-3.0/Maestro.Shared.UI/Maestro.Shared.UI.csproj
===================================================================
--- sandbox/maestro-3.0/Maestro.Shared.UI/Maestro.Shared.UI.csproj	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/Maestro.Shared.UI/Maestro.Shared.UI.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{CFD19053-2172-41D3-8460-0FD2123A1E88}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -38,6 +38,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="CancelException.cs" />
     <Compile Include="CheckBoxBinder.cs" />
     <Compile Include="CollapsiblePanel.cs">
       <SubType>UserControl</SubType>
@@ -54,6 +55,12 @@
     </Compile>
     <Compile Include="TextBoxBinder.cs" />
     <Compile Include="WaitCursor.cs" />
+    <Compile Include="ProgressDialog.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="ProgressDialog.designer.cs">
+      <DependentUpon>ProgressDialog.cs</DependentUpon>
+    </Compile>
     <Service Include="{94E38DFF-614B-4cbd-B67C-F211BB35CE8B}" />
   </ItemGroup>
   <ItemGroup>
@@ -66,6 +73,9 @@
       <LastGenOutput>Resources.Designer.cs</LastGenOutput>
       <SubType>Designer</SubType>
     </EmbeddedResource>
+    <EmbeddedResource Include="ProgressDialog.resx">
+      <DependentUpon>ProgressDialog.cs</DependentUpon>
+    </EmbeddedResource>
   </ItemGroup>
   <ItemGroup>
     <None Include="Resources\minus-white.png" />

Added: sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.Designer.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.Designer.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.Designer.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,88 @@
+namespace Maestro.Shared.UI
+{
+    partial class ProgressDialog
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ProgressDialog));
+            this.progressBar1 = new System.Windows.Forms.ProgressBar();
+            this.CancelBtn = new System.Windows.Forms.Button();
+            this.BackgroundWorker = new System.ComponentModel.BackgroundWorker();
+            this.label1 = new System.Windows.Forms.Label();
+            this.SuspendLayout();
+            // 
+            // progressBar1
+            // 
+            resources.ApplyResources(this.progressBar1, "progressBar1");
+            this.progressBar1.Name = "progressBar1";
+            // 
+            // CancelBtn
+            // 
+            resources.ApplyResources(this.CancelBtn, "CancelBtn");
+            this.CancelBtn.Name = "CancelBtn";
+            this.CancelBtn.UseVisualStyleBackColor = true;
+            this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click);
+            // 
+            // BackgroundWorker
+            // 
+            this.BackgroundWorker.WorkerReportsProgress = true;
+            this.BackgroundWorker.WorkerSupportsCancellation = true;
+            this.BackgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BackgroundWorker_DoWork);
+            this.BackgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.BackgroundWorker_RunWorkerCompleted);
+            this.BackgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.BackgroundWorker_ProgressChanged);
+            // 
+            // label1
+            // 
+            resources.ApplyResources(this.label1, "label1");
+            this.label1.Name = "label1";
+            // 
+            // WaitForOperation
+            // 
+            resources.ApplyResources(this, "$this");
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.Controls.Add(this.label1);
+            this.Controls.Add(this.CancelBtn);
+            this.Controls.Add(this.progressBar1);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedToolWindow;
+            this.KeyPreview = true;
+            this.Name = "WaitForOperation";
+            this.Load += new System.EventHandler(this.WaitForOperation_Load);
+            this.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.WaitForOperation_KeyPress);
+            this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.WaitForOperation_FormClosing);
+            this.ResumeLayout(false);
+            this.PerformLayout();
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.ProgressBar progressBar1;
+        private System.ComponentModel.BackgroundWorker BackgroundWorker;
+        private System.Windows.Forms.Label label1;
+        public System.Windows.Forms.Button CancelBtn;
+    }
+}
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.cs
===================================================================
--- sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.cs	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,163 @@
+#region Disclaimer / License
+// Copyright (C) 2009, Kenneth Skovhede
+// http://www.hexad.dk, opensource at 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
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Text;
+using System.Windows.Forms;
+
+namespace Maestro.Shared.UI
+{
+    public partial class ProgressDialog : Form
+    {
+        public delegate object DoBackgroundWork(BackgroundWorker worker, DoWorkEventArgs e, params object[] args);
+
+        private DoBackgroundWork m_method;
+        private object[] m_args;
+        private object m_result;
+        private bool m_cancelAborts = false;
+
+        private System.Threading.Thread m_worker;
+
+        public ProgressDialog()
+        {
+            InitializeComponent();
+        }
+
+        /// <summary>
+        /// A value indicating if the cancel button attempts to abort the thread, rather than simply flag it for cancellation
+        /// </summary>
+        public bool CancelAbortsThread 
+        {
+            get { return m_cancelAborts; }
+            set { m_cancelAborts = value; }
+        }
+
+        public object RunOperationAsync(Form owner, DoBackgroundWork method, params object[] arguments)
+        {
+            m_method = method;
+            m_args = arguments;
+            if (this.Visible)
+                this.Hide();
+
+            if (this.ShowDialog(owner) == DialogResult.OK)
+                return m_result;
+            else
+                throw new CancelException();
+        }
+
+        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
+        {
+            try
+            {
+                m_worker = System.Threading.Thread.CurrentThread;
+                e.Result = m_method(BackgroundWorker, e, m_args);
+            }
+            catch (System.Threading.ThreadAbortException)
+            {
+                e.Cancel = true;
+                e.Result = null;
+                //We exit, but hide the abort details from the BackgroundWorker, so it processes events as it should
+                System.Threading.Thread.ResetAbort();
+            }
+            finally
+            {
+                m_worker = null;
+            }
+        }
+
+        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
+        {
+            if (e.ProgressPercentage < 0)
+            {
+                progressBar1.Style = ProgressBarStyle.Marquee;
+            }
+            else
+            {
+                if (progressBar1.Style == ProgressBarStyle.Marquee)
+                    progressBar1.Style = ProgressBarStyle.Blocks;
+
+                progressBar1.Value = Math.Min(Math.Max(progressBar1.Minimum, e.ProgressPercentage), progressBar1.Maximum);
+            }
+
+            if (e.UserState != null)
+                label1.Text = e.UserState.ToString();
+        }
+
+        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
+        {
+            if (e.Cancelled)
+            {
+                this.DialogResult = DialogResult.Cancel;
+                this.Close();
+            }
+            else if (e.Error != null)
+            {
+                throw e.Error;
+            }
+            else
+            {
+                m_result = e.Result;
+                this.DialogResult = DialogResult.OK;
+                this.Close();
+            }
+        }
+
+        private void WaitForOperation_Load(object sender, EventArgs e)
+        {
+            BackgroundWorker.WorkerSupportsCancellation = !m_cancelAborts;
+            BackgroundWorker.RunWorkerAsync();
+        }
+
+        private void CancelBtn_Click(object sender, EventArgs e)
+        {
+            CancelBtn.Enabled = false;
+            if (!BackgroundWorker.CancellationPending && BackgroundWorker.WorkerSupportsCancellation)
+                BackgroundWorker.CancelAsync();
+
+            if (m_cancelAborts)
+            {
+                try
+                {
+                    //Protected, because threading can make it null after the check
+                    if (m_worker != null && m_worker.IsAlive)
+                        m_worker.Abort();
+                }
+                catch
+                {
+                }
+            }
+        }
+
+        private void WaitForOperation_FormClosing(object sender, FormClosingEventArgs e)
+        {
+            if (e.CloseReason == CloseReason.UserClosing)
+                CancelBtn_Click(sender, e);
+        }
+
+        private void WaitForOperation_KeyPress(object sender, KeyPressEventArgs e)
+        {
+            if (e.KeyChar == (char)Keys.Escape && CancelBtn.Enabled)
+                CancelBtn_Click(sender, e);
+        }
+    }
+}
\ No newline at end of file

Added: sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.resx
===================================================================
--- sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.resx	                        (rev 0)
+++ sandbox/maestro-3.0/Maestro.Shared.UI/ProgressDialog.resx	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <assembly alias="System.Drawing" name="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
+  <data name="progressBar1.Location" type="System.Drawing.Point, System.Drawing">
+    <value>24, 40</value>
+  </data>
+  <data name="progressBar1.Size" type="System.Drawing.Size, System.Drawing">
+    <value>360, 23</value>
+  </data>
+  <assembly alias="mscorlib" name="mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="progressBar1.TabIndex" type="System.Int32, mscorlib">
+    <value>0</value>
+  </data>
+  <data name="&gt;&gt;progressBar1.Name" xml:space="preserve">
+    <value>progressBar1</value>
+  </data>
+  <data name="&gt;&gt;progressBar1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.ProgressBar, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;progressBar1.Parent" xml:space="preserve">
+    <value>$this</value>
+  </data>
+  <data name="&gt;&gt;progressBar1.ZOrder" xml:space="preserve">
+    <value>2</value>
+  </data>
+  <data name="CancelBtn.Location" type="System.Drawing.Point, System.Drawing">
+    <value>168, 72</value>
+  </data>
+  <data name="CancelBtn.Size" type="System.Drawing.Size, System.Drawing">
+    <value>75, 23</value>
+  </data>
+  <data name="CancelBtn.TabIndex" type="System.Int32, mscorlib">
+    <value>1</value>
+  </data>
+  <data name="CancelBtn.Text" xml:space="preserve">
+    <value>Cancel</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Name" xml:space="preserve">
+    <value>CancelBtn</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.Parent" xml:space="preserve">
+    <value>$this</value>
+  </data>
+  <data name="&gt;&gt;CancelBtn.ZOrder" xml:space="preserve">
+    <value>1</value>
+  </data>
+  <metadata name="BackgroundWorker.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
+    <value>17, 17</value>
+  </metadata>
+  <data name="label1.AutoSize" type="System.Boolean, mscorlib">
+    <value>True</value>
+  </data>
+  <data name="label1.Location" type="System.Drawing.Point, System.Drawing">
+    <value>24, 16</value>
+  </data>
+  <data name="label1.Size" type="System.Drawing.Size, System.Drawing">
+    <value>56, 13</value>
+  </data>
+  <data name="label1.TabIndex" type="System.Int32, mscorlib">
+    <value>2</value>
+  </data>
+  <data name="label1.Text" xml:space="preserve">
+    <value>Working...</value>
+  </data>
+  <data name="&gt;&gt;label1.Name" xml:space="preserve">
+    <value>label1</value>
+  </data>
+  <data name="&gt;&gt;label1.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;label1.Parent" xml:space="preserve">
+    <value>$this</value>
+  </data>
+  <data name="&gt;&gt;label1.ZOrder" xml:space="preserve">
+    <value>0</value>
+  </data>
+  <metadata name="$this.Localizable" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
+    <value>True</value>
+  </metadata>
+  <data name="$this.AutoScaleDimensions" type="System.Drawing.SizeF, System.Drawing">
+    <value>6, 13</value>
+  </data>
+  <data name="$this.ClientSize" type="System.Drawing.Size, System.Drawing">
+    <value>412, 103</value>
+  </data>
+  <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
+  <data name="$this.StartPosition" type="System.Windows.Forms.FormStartPosition, System.Windows.Forms">
+    <value>CenterParent</value>
+  </data>
+  <data name="$this.Text" xml:space="preserve">
+    <value>Wait for operation to complete</value>
+  </data>
+  <data name="&gt;&gt;BackgroundWorker.Name" xml:space="preserve">
+    <value>BackgroundWorker</value>
+  </data>
+  <data name="&gt;&gt;BackgroundWorker.Type" xml:space="preserve">
+    <value>System.ComponentModel.BackgroundWorker, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+  <data name="&gt;&gt;$this.Name" xml:space="preserve">
+    <value>WaitForOperation</value>
+  </data>
+  <data name="&gt;&gt;$this.Type" xml:space="preserve">
+    <value>System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </data>
+</root>
\ No newline at end of file

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/OSGeo.MapGuide.MaestroAPI.csproj	2010-07-21 10:14:11 UTC (rev 5045)
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
-    <ProductVersion>9.0.21022</ProductVersion>
+    <ProductVersion>9.0.30729</ProductVersion>
     <SchemaVersion>2.0</SchemaVersion>
     <ProjectGuid>{80FA3158-8B5F-48D1-A393-0378AFE48A7E}</ProjectGuid>
     <OutputType>Library</OutputType>
@@ -213,11 +213,13 @@
     <Compile Include="Resource\Conversion\ResourceConverter.cs" />
     <Compile Include="Resource\Conversion\ResourceUpgrader.cs" />
     <Compile Include="Resource\IResource.cs" />
+    <Compile Include="Resource\IResourceValidator.cs" />
     <Compile Include="Resource\IVersionedEntity.cs" />
     <Compile Include="Resource\ResourceIdentifier.cs" />
     <Compile Include="Resource\ResourceSchemaChain.cs" />
     <Compile Include="Resource\ResourceTypeDescriptor.cs" />
     <Compile Include="Resource\ResourceContentVersionChecker.cs" />
+    <Compile Include="Resource\ResourceValidatorSet.cs" />
     <Compile Include="Serialization\Enums.cs" />
     <Compile Include="Serialization\IBinarySerializeable.cs" />
     <Compile Include="Serialization\MgBinaryDeserializer.cs" />

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/ApplicationDefinition.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/ApplicationDefinition.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/ApplicationDefinition.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -23,9 +23,27 @@
 using OSGeo.MapGuide.MaestroAPI.Resource;
 using System.Xml.Serialization;
 using OSGeo.MapGuide.MaestroAPI;
+using System.Xml;
 
 namespace OSGeo.MapGuide.ObjectModels.ApplicationDefinition
 {
+    partial class CustomContentType
+    {
+        public XmlElement GetElement(string name)
+        {
+            if (this.anyField == null)
+                return null;
+
+            foreach(XmlElement el in this.anyField)
+            {
+                if (el.Name == name)
+                    return el;
+            }
+
+            return null;
+        }
+    }
+
     partial class ApplicationDefinitionType : IResource
     {
         internal ApplicationDefinitionType() { }

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/Envelope.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/Envelope.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/Envelope.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -81,5 +81,32 @@
             if (env.MaxY > this.MaxY)
                 this.MaxY = env.MaxY;
         }
+
+        /// <summary>
+        /// Indicates whether the specified coordinates are within this instance
+        /// </summary>
+        /// <param name="x"></param>
+        /// <param name="y"></param>
+        /// <returns></returns>
+        public bool Contains(double x, double y)
+        {
+            return this.MinX <= x &&
+                   this.MaxX >= x &&
+                   this.MinY <= y &&
+                   this.MaxY >= y;
+        }
+
+        /// <summary>
+        /// Indicates whether the specified envelope intersects this instance
+        /// </summary>
+        /// <param name="other"></param>
+        /// <returns></returns>
+        public bool Intersects(Envelope other)
+        {
+            if (other == null)
+                return false;
+
+            return !(other.MinX > this.MaxX || other.MaxX < this.MinX || other.MinY > this.MaxY || other.MaxY < this.MinY);
+        }
     }
 }

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/FeatureSource.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/FeatureSource.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/FeatureSource.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -23,6 +23,7 @@
 using OSGeo.MapGuide.MaestroAPI.Resource;
 using System.Xml.Serialization;
 using OSGeo.MapGuide.MaestroAPI;
+using OSGeo.MapGuide.ObjectModels.Common;
 
 namespace OSGeo.MapGuide.ObjectModels.FeatureSource
 {
@@ -263,5 +264,51 @@
         {
             return this.CurrentConnection.FeatureService.DescribeFeatureSource(this.ResourceID);
         }
+
+        /// <summary>
+        /// Convenience method to retrieve the spatial context information of this feature source
+        /// </summary>
+        /// <returns></returns>
+        public OSGeo.MapGuide.ObjectModels.Common.FdoSpatialContextList GetSpatialInfo()
+        {
+            return this.CurrentConnection.FeatureService.GetSpatialContextInfo(this.ResourceID, false);
+        }
+
+        /// <summary>
+        /// Convenience methods to get the identity properties of a given feature class (name)
+        /// </summary>
+        /// <param name="className"></param>
+        /// <returns></returns>
+        public string[] GetIdentityProperties(string className)
+        {
+            try
+            {
+                return this.CurrentConnection.FeatureService.GetIdentityProperties(this.ResourceID, className);
+            }
+            catch (Exception ex)
+            {
+                //MgClassNotFoundException is thrown for classes w/ no identity properties
+                //when the correct server response should be an empty array
+                if (ex.Message.IndexOf("MgClassNotFoundException") >= 0)
+                {
+                    return new string[0];
+                }
+                else
+                {
+                    throw;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Convenience method to get the spatial extents of a given feature class
+        /// </summary>
+        /// <param name="className"></param>
+        /// <param name="geomProperty"></param>
+        /// <returns></returns>
+        public Envelope GetSpatialExtent(string className, string geomProperty)
+        {
+            return this.CurrentConnection.FeatureService.GetSpatialExtent(this.ResourceID, className, geomProperty);
+        }
     }
 }

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/LayerDefinition.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/LayerDefinition.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/ObjectModels/LayerDefinition.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -24,14 +24,164 @@
 using System.Xml.Serialization;
 using OSGeo.MapGuide.MaestroAPI;
 using OSGeo.MapGuide.ObjectModels.Common;
+using System.ComponentModel;
 
 namespace OSGeo.MapGuide.ObjectModels.LayerDefinition
 {
+    public enum LayerType
+    {
+        Drawing,
+        Vector,
+        Raster
+    }
+
     public interface ILayerDefinition : IResource
     {
         Envelope GetSpatialExtent(bool allowFallbackToContextInformation);
+
+        ISubLayerDefinition SubLayer { get; }
     }
 
+    public interface ISubLayerDefinition
+    {
+        LayerType LayerType { get; } 
+    }
+
+    abstract partial class BaseLayerDefinitionType : ISubLayerDefinition
+    {
+        [XmlIgnore]
+        public abstract LayerType LayerType { get; }
+    }
+
+    public interface IVectorLayerDefinition : ISubLayerDefinition
+    {
+        string FeatureSourceID { get; set; }
+
+        string FullQualifiedClassName { get; set; }
+
+        string GeometryProperty { get; set; }
+
+        string Url { get; set; }
+
+        string ToolTip { get; set; }
+
+        string Filter { get; set; }
+    }
+
+    public interface IRasterLayerDefinition : ISubLayerDefinition
+    {
+        string FeatureSourceID { get; set; }
+    }
+
+    public interface IDrawingLayerDefinition : ISubLayerDefinition
+    {
+        string DrawingSourceID { get; set; }
+    }
+
+    partial class VectorLayerDefinitionType : IVectorLayerDefinition
+    {
+        public override LayerType LayerType
+        {
+            get { return LayerType.Vector; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.FeatureSourceID
+        {
+            get { return this.ResourceId; }
+            set { this.ResourceId = value; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.FullQualifiedClassName
+        {
+            get { return this.FeatureName; }
+            set { this.FeatureName = value; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.GeometryProperty
+        {
+            get { return this.Geometry; }
+            set { this.Geometry = value; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.Url
+        {
+            get { return this.Url; }
+            set { this.Url = value; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.ToolTip
+        {
+            get { return this.ToolTip; }
+            set { this.ToolTip = value; }
+        }
+
+        [XmlIgnore]
+        string IVectorLayerDefinition.Filter
+        {
+            get { return this.Filter; }
+            set { this.Filter = value; }
+        }
+    }
+
+    partial class GridLayerDefinitionType : IRasterLayerDefinition
+    {
+        public override LayerType LayerType
+        {
+            get { return LayerType.Raster; }
+        }
+
+        [XmlIgnore]
+        string IRasterLayerDefinition.FeatureSourceID
+        {
+            get { return this.ResourceId; }
+            set { this.ResourceId = value; }
+        }
+    }
+
+    partial class DrawingLayerDefinitionType : IDrawingLayerDefinition
+    {
+        public override LayerType LayerType
+        {
+            get { return LayerType.Drawing; }
+        }
+
+        string IDrawingLayerDefinition.DrawingSourceID
+        {
+            get { return this.ResourceId; }
+            set { this.ResourceId = value; }
+        }
+    }
+
+    partial class VectorScaleRangeType
+    {
+        #region Missing generated stuff
+        [EditorBrowsable(EditorBrowsableState.Never)]
+        private bool minScaleFieldSpecified;
+
+        [System.Xml.Serialization.XmlIgnoreAttribute()]
+        public bool MinScaleSpecified
+        {
+            get
+            {
+                return this.minScaleFieldSpecified;
+            }
+            set
+            {
+                if ((minScaleFieldSpecified.Equals(value) != true))
+                {
+                    this.minScaleFieldSpecified = value;
+                    this.OnPropertyChanged("MinScaleSpecified");
+                }
+            }
+        }
+        #endregion
+    }
+
     partial class LayerDefinition : ILayerDefinition
     {
         internal LayerDefinition() { } 
@@ -125,5 +275,10 @@
             else
                 return null;
         }
+
+        public ISubLayerDefinition SubLayer
+        {
+            get { return this.Item; }
+        }
     }
 }

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.Designer.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.Designer.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.Designer.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -124,6 +124,15 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to There is no registered validator for {0} v{1}. Validation cannot be performed.
+        /// </summary>
+        internal static string ERR_NO_REGISTERED_VALIDATOR {
+            get {
+                return ResourceManager.GetString("ERR_NO_REGISTERED_VALIDATOR", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to Could not find matching serializer for this resource: .
         /// </summary>
         internal static string ERR_NO_SERIALIZER {

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.resx
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.resx	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Properties/Resources.resx	2010-07-21 10:14:11 UTC (rev 5045)
@@ -138,6 +138,10 @@
   <data name="ERR_NO_DOWNGRADE_PATH" xml:space="preserve">
     <value>There is no downgrade path to the desired resource version</value>
   </data>
+  <data name="ERR_NO_REGISTERED_VALIDATOR" xml:space="preserve">
+    <value>There is no registered validator for {0} v{1}. Validation cannot be performed</value>
+    <comment>A warning message that is displayed if there is no registered validator for the specified resource and version</comment>
+  </data>
   <data name="ERR_NO_SERIALIZER" xml:space="preserve">
     <value>Could not find matching serializer for this resource: </value>
   </data>

Added: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/IResourceValidator.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/IResourceValidator.cs	                        (rev 0)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/IResourceValidator.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,79 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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;
+
+namespace OSGeo.MapGuide.MaestroAPI.Resource
+{
+    public interface IResourceValidator
+    {
+        ResourceTypeDescriptor SupportedResourceAndVersion { get; }
+
+        ValidationIssue[] Validate(IResource resource, bool recurse);
+    }
+
+    /// <summary>
+    /// Represents a validation issue collected during validation
+    /// </summary>
+    public class ValidationIssue
+    {
+        public ValidationIssue(IResource res, ValidationStatus stat, string msg)
+        {
+            this.Resource = res;
+            this.Status = stat;
+            this.Message = msg;
+        }
+
+        /// <summary>
+        /// Gets the message for the validation issue
+        /// </summary>
+        public string Message { get; private set; }
+
+        /// <summary>
+        /// Gets the status of the validation issue
+        /// </summary>
+        public ValidationStatus Status { get; private set; }
+
+        /// <summary>
+        /// Gets the resource this issue pertains to
+        /// </summary>
+        public IResource Resource { get; private set; }
+    }
+
+    /// <summary>
+    /// All possible states a validation issue may have
+    /// </summary>
+    public enum ValidationStatus
+    {
+        /// <summary>
+        /// Indicates that the issue is non-vital, eg. a performance problem
+        /// </summary>
+        Information,
+        /// <summary>
+        /// Indicates that the issue is likely to cause problems
+        /// </summary>
+        Warning,
+        /// <summary>
+        /// Indicates that the issue will prevent correct operation of the map
+        /// </summary>
+        Error
+    }
+}

Added: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/ResourceValidatorSet.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/ResourceValidatorSet.cs	                        (rev 0)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI/Resource/ResourceValidatorSet.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -0,0 +1,92 @@
+#region Disclaimer / License
+// Copyright (C) 2010, Jackie Ng
+// http://trac.osgeo.org/mapguide/wiki/maestro, jumpinjackie at 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.Exceptions;
+
+namespace OSGeo.MapGuide.MaestroAPI.Resource
+{
+    /// <summary>
+    /// A collection of resource validators
+    /// </summary>
+    public static class ResourceValidatorSet
+    {
+        private static List<IResourceValidator> m_validators = new List<IResourceValidator>();
+
+        public static void RegisterValidator(IResourceValidator v)
+        {
+            if (!m_validators.Contains(v))
+                m_validators.Add(v);
+        }
+
+        public static ValidationIssue[] Validate(IEnumerable<IResource> items, bool recurse)
+        {
+            var issues = new List<ValidationIssue>();
+            foreach (var item in items)
+            {
+                issues.AddRange(Validate(item, true));
+            }
+            return issues.ToArray();
+        }
+
+        public static ValidationIssue[] Validate(IResource item, bool recurse)
+        {
+            List<ValidationIssue> issues = new List<ValidationIssue>();
+            if (!HasValidator(item.ResourceType, item.ResourceVersion))
+            {
+                issues.Add(new ValidationIssue(item, ValidationStatus.Warning, string.Format(Properties.Resources.ERR_NO_REGISTERED_VALIDATOR, item.ResourceType, item.ResourceVersion)));
+            }
+            else
+            {
+                foreach (IResourceValidator v in m_validators)
+                {
+                    try
+                    {
+                        ValidationIssue[] tmp = v.Validate(item, recurse);
+                        if (tmp != null)
+                            issues.AddRange(tmp);
+                    }
+                    catch (Exception ex)
+                    {
+                        string msg = NestedExceptionMessageProcessor.GetFullMessage(ex);
+                        issues.Add(new ValidationIssue(item, ValidationStatus.Error, "Failed in validator: " + msg));
+                    }
+                }
+            }
+            return issues.ToArray();
+        }
+
+        public static bool HasValidator(ResourceTypes resourceTypes, Version version)
+        {
+            bool found = false;
+            var find = new ResourceTypeDescriptor(resourceTypes, version.ToString());
+            foreach (var v in m_validators)
+            {
+                if (v.SupportedResourceAndVersion.Equals(find))
+                {
+                    found = true;
+                    break;
+                }
+            }
+            return found;
+        }
+    }
+}

Modified: sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs
===================================================================
--- sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs	2010-07-21 07:04:30 UTC (rev 5044)
+++ sandbox/maestro-3.0/OSGeo.MapGuide.MaestroAPI.Http/HttpServerConnection.cs	2010-07-21 10:14:11 UTC (rev 5045)
@@ -36,6 +36,8 @@
 using OSGeo.MapGuide.MaestroAPI.Http;
 using System.IO;
 using OSGeo.MapGuide.ObjectModels.Capabilities;
+using System.Text;
+using System.Collections.Generic;
 
 namespace OSGeo.MapGuide.MaestroAPI
 {
@@ -259,19 +261,32 @@
 
 		public override string TestConnection(string featuresource)
 		{
-			string req = m_reqBuilder.TestConnection(featuresource);
-				
-			try
-			{
-				byte[] x = this.DownloadData(req);
-			}
-			catch (WebException wex)
-			{
+            string req = m_reqBuilder.TestConnection(featuresource);
+            string result = string.Empty;
+            try
+            {
+                byte[] x = this.DownloadData(req);
+                //UGLY: Prune out the '\0' chars
+                List<byte> bytes = new List<byte>();
+                for (int i = 0; i < x.Length; i++)
+                {
+                    if (x[i] > 0)
+                    {
+                        bytes.Add(x[i]);
+                    }
+                    else
+                    {
+                        break;
+                    }
+                }
+                result = Encoding.UTF8.GetString(bytes.ToArray());
+            }
+            catch (WebException wex)
+            {
                 if (wex.Response != null)
                 {
                     try
                     {
-                        string result = "";
                         using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
                         {
                             Utility.CopyStream(wex.Response.GetResponseStream(), ms);
@@ -291,35 +306,35 @@
                     }
                 }
 
-				if (wex.InnerException == null)
-					return wex.Message;
-				else
-					return wex.InnerException.Message;
-			}
-			catch (Exception ex)
-			{
-                return NestedExceptionMessageProcessor.GetFullMessage(ex);
-			}
+                if (wex.InnerException == null)
+                    return wex.Message;
+                else
+                    return wex.InnerException.Message;
+            }
+            catch (Exception ex)
+            {
+                result = NestedExceptionMessageProcessor.GetFullMessage(ex);
+            }
 
-			return string.Empty;
+            return result;
 		}
 
 		public string TestConnection(string providername, NameValueCollection parameters)
 		{
-			string req = m_reqBuilder.TestConnection(providername, parameters);
-			//System.IO.MemoryStream msx = new System.IO.MemoryStream();
-			//System.Net.WebRequest reqp = m_reqBuilder.TestConnectionPost(providername, parameters, msx);
-				
-			try
-			{
-				/*msx.Position = 0;
-				Utility.CopyStream(msx, reqp.GetRequestStream());
-				reqp.GetRequestStream().Flush();
-				int f = reqp.GetResponse().GetResponseStream().ReadByte();*/
-				byte[] x = this.DownloadData(req);
-			}
-			catch (WebException wex)
-			{
+            string req = m_reqBuilder.TestConnection(providername, parameters);
+            //System.IO.MemoryStream msx = new System.IO.MemoryStream();
+            //System.Net.WebRequest reqp = m_reqBuilder.TestConnectionPost(providername, parameters, msx);
+
+            try
+            {
+                /*msx.Position = 0;
+                Utility.CopyStream(msx, reqp.GetRequestStream());
+                reqp.GetRequestStream().Flush();
+                int f = reqp.GetResponse().GetResponseStream().ReadByte();*/
+                byte[] x = this.DownloadData(req);
+            }
+            catch (WebException wex)
+            {
                 if (wex.Response != null)
                 {
                     try
@@ -344,17 +359,17 @@
                     }
                 }
 
-				if (wex.InnerException == null)
-					return wex.Message;
-				else
-					return wex.InnerException.Message;
-			}
-			catch (Exception ex)
-			{
+                if (wex.InnerException == null)
+                    return wex.Message;
+                else
+                    return wex.InnerException.Message;
+            }
+            catch (Exception ex)
+            {
                 return NestedExceptionMessageProcessor.GetFullMessage(ex);
-			}
+            }
 
-			return string.Empty;
+            return string.Empty;
 		}
 
 		/// <summary>



More information about the mapguide-commits mailing list